Merge branch 'master' of github.com:highfidelity/hifi into persist-entities-as-json

This commit is contained in:
Seth Alves 2015-03-16 12:13:26 -07:00
commit c90ca4b425
11 changed files with 457 additions and 131 deletions

View file

@ -103,12 +103,24 @@ var isActive = false;
var placingEntityID = null;
IMPORTING_SVO_OVERLAY_WIDTH = 130;
IMPORTING_SVO_OVERLAY_WIDTH = 144;
IMPORTING_SVO_OVERLAY_HEIGHT = 30;
IMPORTING_SVO_OVERLAY_MARGIN = 6;
var importingSVOOverlay = Overlays.addOverlay("text", {
IMPORTING_SVO_OVERLAY_MARGIN = 5;
IMPORTING_SVO_OVERLAY_LEFT_MARGIN = 34;
var importingSVOImageOverlay = Overlays.addOverlay("image", {
imageURL: HIFI_PUBLIC_BUCKET + "images/hourglass.svg",
width: 20,
height: 20,
alpha: 1.0,
color: { red: 255, green: 255, blue: 255 },
x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH,
y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT,
visible: false,
});
var importingSVOTextOverlay = Overlays.addOverlay("text", {
font: { size: 14 },
text: "Importing SVO...",
leftMargin: IMPORTING_SVO_OVERLAY_LEFT_MARGIN,
x: Window.innerWidth - IMPORTING_SVO_OVERLAY_WIDTH - IMPORTING_SVO_OVERLAY_MARGIN,
y: Window.innerHeight - IMPORTING_SVO_OVERLAY_HEIGHT - IMPORTING_SVO_OVERLAY_MARGIN,
width: IMPORTING_SVO_OVERLAY_WIDTH,
@ -754,6 +766,10 @@ function setupModelMenus() {
afterItem: "Allow Selecting of Large Models", isCheckable: true, isChecked: true });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Lights", shortcutKey: "CTRL+SHIFT+META+L",
afterItem: "Allow Selecting of Small Models", isCheckable: true });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities In Box", shortcutKey: "CTRL+SHIFT+META+A",
afterItem: "Allow Selecting of Lights" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Select All Entities Touching Box", shortcutKey: "CTRL+SHIFT+META+T",
afterItem: "Select All Entities In Box" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Models", isSeparator: true, beforeItem: "Settings" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Entities", shortcutKey: "CTRL+META+E", afterItem: "Models" });
@ -783,6 +799,8 @@ function cleanupModelMenus() {
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Lights");
Menu.removeMenuItem("Edit", "Select All Entities In Box");
Menu.removeMenuItem("Edit", "Select All Entities Touching Box");
Menu.removeSeparator("File", "Models");
Menu.removeMenuItem("File", "Export Entities");
@ -807,7 +825,8 @@ Script.scriptEnding.connect(function() {
selectionDisplay.cleanup();
Entities.setLightsArePickable(originalLightsArePickable);
Overlays.deleteOverlay(importingSVOOverlay);
Overlays.deleteOverlay(importingSVOImageOverlay);
Overlays.deleteOverlay(importingSVOTextOverlay);
});
// Do some stuff regularly, like check for placement of various overlays
@ -817,6 +836,45 @@ Script.update.connect(function (deltaTime) {
selectionDisplay.checkMove();
});
function insideBox(center, dimensions, point) {
return (Math.abs(point.x - center.x) <= (dimensions.x / 2.0))
&& (Math.abs(point.y - center.y) <= (dimensions.y / 2.0))
&& (Math.abs(point.z - center.z) <= (dimensions.z / 2.0));
}
function selectAllEtitiesInCurrentSelectionBox(keepIfTouching) {
if (selectionManager.hasSelection()) {
// Get all entities touching the bounding box of the current selection
var boundingBoxCorner = Vec3.subtract(selectionManager.worldPosition,
Vec3.multiply(selectionManager.worldDimensions, 0.5));
var entities = Entities.findEntitiesInBox(boundingBoxCorner, selectionManager.worldDimensions);
if (!keepIfTouching) {
var isValid;
if (selectionManager.localPosition === null) {
isValid = function(position) {
return insideBox(selectionManager.worldPosition, selectionManager.worldDimensions, position);
}
} else {
isValid = function(position) {
var localPosition = Vec3.multiplyQbyV(Quat.inverse(selectionManager.localRotation),
Vec3.subtract(position,
selectionManager.localPosition));
return insideBox({ x: 0, y: 0, z: 0 }, selectionManager.localDimensions, localPosition);
}
}
for (var i = 0; i < entities.length; ++i) {
var properties = Entities.getEntityProperties(entities[i]);
if (!isValid(properties.position)) {
entities.splice(i, 1);
--i;
}
}
}
selectionManager.setSelections(entities);
}
}
function deleteSelectedEntities() {
if (SelectionManager.hasSelection()) {
print(" Delete Entities");
@ -875,6 +933,10 @@ function handeMenuEvent(menuItem) {
}
} else if (menuItem == "Entity List...") {
entityListTool.toggleVisible();
} else if (menuItem == "Select All Entities In Box") {
selectAllEtitiesInCurrentSelectionBox(false);
} else if (menuItem == "Select All Entities Touching Box") {
selectAllEtitiesInCurrentSelectionBox(true);
} else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) {
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
}
@ -882,7 +944,8 @@ function handeMenuEvent(menuItem) {
}
function importSVO(importURL) {
Overlays.editOverlay(importingSVOOverlay, { visible: true });
Overlays.editOverlay(importingSVOTextOverlay, { visible: true });
Overlays.editOverlay(importingSVOImageOverlay, { visible: true });
var success = Clipboard.importEntities(importURL);
@ -907,7 +970,8 @@ function importSVO(importURL) {
Window.alert("There was an error importing the entity file.");
}
Overlays.editOverlay(importingSVOOverlay, { visible: false });
Overlays.editOverlay(importingSVOTextOverlay, { visible: false });
Overlays.editOverlay(importingSVOImageOverlay, { visible: false });
}
Window.svoImportRequested.connect(importSVO);

View file

@ -106,6 +106,9 @@ var usersWindow = (function () {
var displayText = "",
myUsername,
user,
userText,
textWidth,
maxTextWidth,
i;
myUsername = GlobalServices.username;
@ -113,12 +116,27 @@ var usersWindow = (function () {
for (i = 0; i < usersOnline.length; i += 1) {
user = usersOnline[i];
if (user.username !== myUsername && user.online) {
usersOnline[i].usernameWidth = Overlays.textSize(windowPane2D, user.username).width;
linesOfUsers.push(i);
displayText += "\n" + user.username;
userText = user.username;
if (user.location.root) {
displayText += " @ " + user.location.root.name;
userText += " @ " + user.location.root.name;
}
textWidth = Overlays.textSize(windowPane2D, userText).width;
maxTextWidth = WINDOW_WIDTH_2D - 2 * WINDOW_MARGIN_2D;
if (textWidth > maxTextWidth) {
// Trim and append "..." to fit window width
maxTextWidth = maxTextWidth - Overlays.textSize(windowPane2D, "...").width;
while (textWidth > maxTextWidth) {
userText = userText.slice(0, -1);
textWidth = Overlays.textSize(windowPane2D, userText).width;
}
userText += "...";
textWidth = Overlays.textSize(windowPane2D, userText).width;
}
usersOnline[i].textWidth = textWidth;
linesOfUsers.push(i);
displayText += "\n" + userText;
}
}
@ -253,7 +271,7 @@ var usersWindow = (function () {
}
if (0 <= lineClicked && lineClicked < linesOfUsers.length
&& overlayX <= usersOnline[linesOfUsers[lineClicked]].usernameWidth) {
&& 0 <= overlayX && overlayX <= usersOnline[linesOfUsers[lineClicked]].textWidth) {
//print("Go to " + usersOnline[linesOfUsers[lineClicked]].username);
location.goToUser(usersOnline[linesOfUsers[lineClicked]].username);
}
@ -262,7 +280,7 @@ var usersWindow = (function () {
visibilityChanged = false;
for (i = 0; i < visibilityControls2D.length; i += 1) {
// Don't need to test radioOverlay if it us under textOverlay.
if (clickedOverlay === visibilityControls2D[i].textOverlay) {
if (clickedOverlay === visibilityControls2D[i].textOverlay && event.x <= visibilityControls2D[i].optionWidth) {
GlobalServices.findableBy = VISIBILITY_VALUES[i];
visibilityChanged = true;
}
@ -286,7 +304,8 @@ var usersWindow = (function () {
}
function setUp() {
var textSizeOverlay;
var textSizeOverlay,
optionText;
textSizeOverlay = Overlays.addOverlay("text", { font: WINDOW_FONT_2D, visible: false });
windowTextHeight = Math.floor(Overlays.textSize(textSizeOverlay, "1").height);
@ -351,6 +370,7 @@ var usersWindow = (function () {
myVisibility = "";
}
optionText = "everyone";
visibilityControls2D = [{
radioOverlay: Overlays.addOverlay("image", { // Create first so that it is under textOverlay.
x: WINDOW_MARGIN_2D,
@ -377,24 +397,34 @@ var usersWindow = (function () {
color: WINDOW_HEADING_COLOR_2D,
alpha: WINDOW_FOREGROUND_ALPHA_2D,
backgroundAlpha: 0.0,
text: "everyone",
text: optionText,
font: WINDOW_FONT_2D,
visible: isVisible
}),
selected: myVisibility === VISIBILITY_VALUES[0]
} ];
}];
visibilityControls2D[0].optionWidth = WINDOW_MARGIN_2D + VISIBILITY_RADIO_SPACE
+ Overlays.textSize(visibilityControls2D[0].textOverlay, optionText).width;
optionText = "my friends";
visibilityControls2D[1] = {
radioOverlay: Overlays.cloneOverlay(visibilityControls2D[0].radioOverlay),
textOverlay: Overlays.cloneOverlay(visibilityControls2D[0].textOverlay),
selected: myVisibility === VISIBILITY_VALUES[1]
};
Overlays.editOverlay(visibilityControls2D[1].textOverlay, { text: "my friends" });
Overlays.editOverlay(visibilityControls2D[1].textOverlay, { text: optionText });
visibilityControls2D[1].optionWidth = WINDOW_MARGIN_2D + VISIBILITY_RADIO_SPACE
+ Overlays.textSize(visibilityControls2D[1].textOverlay, optionText).width;
optionText = "no one";
visibilityControls2D[2] = {
radioOverlay: Overlays.cloneOverlay(visibilityControls2D[0].radioOverlay),
textOverlay: Overlays.cloneOverlay(visibilityControls2D[0].textOverlay),
selected: myVisibility === VISIBILITY_VALUES[2]
};
Overlays.editOverlay(visibilityControls2D[2].textOverlay, { text: "no one" });
Overlays.editOverlay(visibilityControls2D[2].textOverlay, { text: optionText });
visibilityControls2D[2].optionWidth = WINDOW_MARGIN_2D + VISIBILITY_RADIO_SPACE
+ Overlays.textSize(visibilityControls2D[2].textOverlay, optionText).width;
updateVisibilityControls();

View file

@ -204,8 +204,7 @@ EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
_entityTree->unlock();
if (closestEntity) {
result.id = closestEntity->getID();
result.isKnownID = true;
result = closestEntity->getEntityItemID();
}
}
return result;
@ -227,10 +226,25 @@ QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& ce
QVector<const EntityItem*> entities;
_entityTree->findEntities(center, radius, entities);
_entityTree->unlock();
foreach (const EntityItem* entity, entities) {
EntityItemID thisEntityItemID(entity->getID(), UNKNOWN_ENTITY_TOKEN, true);
result << thisEntityItemID;
result << entity->getEntityItemID();
}
}
return result;
}
QVector<EntityItemID> EntityScriptingInterface::findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const {
QVector<EntityItemID> result;
if (_entityTree) {
_entityTree->lockForRead();
AABox box(corner, dimensions);
QVector<EntityItem*> entities;
_entityTree->findEntities(box, entities);
_entityTree->unlock();
foreach (const EntityItem* entity, entities) {
result << entity->getEntityItemID();
}
}
return result;

View file

@ -88,10 +88,14 @@ public slots:
/// will return a EntityItemID.isKnownID = false if no models are in the radius
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE EntityItemID findClosestEntity(const glm::vec3& center, float radius) const;
/// finds models within the search sphere specified by the center point and radius
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector<EntityItemID> findEntities(const glm::vec3& center, float radius) const;
/// finds models within the search sphere specified by the center point and radius
/// this function will not find any models in script engine contexts which don't have access to models
Q_INVOKABLE QVector<EntityItemID> findEntitiesInBox(const glm::vec3& corner, const glm::vec3& dimensions) const;
/// If the scripting context has visible entities, this will determine a ray intersection, the results
/// may be inaccurate if the engine is unable to access the visible entities, in which case result.accurate

View file

@ -533,6 +533,35 @@ void EntityTree::findEntities(const AACube& cube, QVector<EntityItem*>& foundEnt
foundEntities.swap(args._foundEntities);
}
class FindEntitiesInBoxArgs {
public:
FindEntitiesInBoxArgs(const AABox& box)
: _box(box), _foundEntities() {
}
AABox _box;
QVector<EntityItem*> _foundEntities;
};
bool EntityTree::findInBoxOperation(OctreeElement* element, void* extraData) {
FindEntitiesInBoxArgs* args = static_cast<FindEntitiesInBoxArgs*>(extraData);
if (element->getAACube().touches(args->_box)) {
EntityTreeElement* entityTreeElement = static_cast<EntityTreeElement*>(element);
entityTreeElement->getEntities(args->_box, args->_foundEntities);
return true;
}
return false;
}
// NOTE: assumes caller has handled locking
void EntityTree::findEntities(const AABox& box, QVector<EntityItem*>& foundEntities) {
FindEntitiesInBoxArgs args(box);
// NOTE: This should use recursion, since this is a spatial operation
recurseTreeWithOperation(findInBoxOperation, &args);
// swap the two lists of entity pointers instead of copy
foundEntities.swap(args._foundEntities);
}
EntityItem* EntityTree::findEntityByID(const QUuid& id) {
EntityItemID entityID(id);
return findEntityByEntityItemID(entityID);

View file

@ -111,12 +111,18 @@ public:
/// \param foundEntities[out] vector of const EntityItem*
/// \remark Side effect: any initial contents in foundEntities will be lost
void findEntities(const glm::vec3& center, float radius, QVector<const EntityItem*>& foundEntities);
/// finds all entities that touch a cube
/// \param cube the query cube in world-frame (meters)
/// \param foundEntities[out] vector of non-const EntityItem*
/// \remark Side effect: any initial contents in entities will be lost
void findEntities(const AACube& cube, QVector<EntityItem*>& foundEntities);
/// finds all entities that touch a box
/// \param box the query box in world-frame (meters)
/// \param foundEntities[out] vector of non-const EntityItem*
/// \remark Side effect: any initial contents in entities will be lost
void findEntities(const AABox& box, QVector<EntityItem*>& foundEntities);
void addNewlyCreatedHook(NewlyCreatedEntityHook* hook);
void removeNewlyCreatedHook(NewlyCreatedEntityHook* hook);
@ -176,6 +182,7 @@ private:
static bool findNearPointOperation(OctreeElement* element, void* extraData);
static bool findInSphereOperation(OctreeElement* element, void* extraData);
static bool findInCubeOperation(OctreeElement* element, void* extraData);
static bool findInBoxOperation(OctreeElement* element, void* extraData);
static bool sendEntitiesOperation(OctreeElement* element, void* extraData);
void notifyNewlyCreatedEntity(const EntityItem& newEntity, const SharedNodePointer& senderNode);

View file

@ -111,6 +111,8 @@ int LightEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY(PROP_EXPONENT, float, _exponent);
READ_ENTITY_PROPERTY(PROP_CUTOFF, float, _cutoff);
(void) ignoredAttenuation; // suppress compiler warning
} else {
READ_ENTITY_PROPERTY(PROP_IS_SPOTLIGHT, bool, _isSpotlight);
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);

View file

@ -31,6 +31,7 @@ public:
};
int nextToken();
const QByteArray& getDatum() const { return _datum; }
bool isNextTokenFloat();
void skipLine() { _device->readLine(); }
void pushBackToken(int token) { _pushedBackToken = token; }
void ungetChar(char ch) { _device->ungetChar(ch); }
@ -91,14 +92,32 @@ int OBJTokenizer::nextToken() {
return NO_TOKEN;
}
bool OBJTokenizer::isNextTokenFloat() {
if (nextToken() != OBJTokenizer::DATUM_TOKEN) {
return false;
}
QByteArray token = getDatum();
pushBackToken(OBJTokenizer::DATUM_TOKEN);
bool ok;
token.toFloat(&ok);
return ok;
}
bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry) {
bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping,
FBXGeometry &geometry, QVector<glm::vec3>& faceNormals, QVector<int>& faceNormalIndexes) {
FBXMesh &mesh = geometry.meshes[0];
mesh.parts.append(FBXMeshPart());
FBXMeshPart &meshPart = mesh.parts.last();
bool sawG = false;
bool result = true;
meshPart.diffuseColor = glm::vec3(1, 1, 1);
meshPart.specularColor = glm::vec3(1, 1, 1);
meshPart.emissiveColor = glm::vec3(0, 0, 0);
meshPart.emissiveParams = glm::vec2(0, 1);
meshPart.shininess = 40;
meshPart.opacity = 1;
meshPart.materialID = QString("dontknow") + QString::number(mesh.parts.count());
meshPart.opacity = 1.0;
meshPart._material = model::MaterialPointer(new model::Material());
@ -131,11 +150,27 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeom
break;
}
float x = std::stof(tokenizer.getDatum().data());
// notice the order of z and y -- in OBJ files, up is the 3rd value
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
float y = std::stof(tokenizer.getDatum().data());
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
float z = std::stof(tokenizer.getDatum().data());
while (tokenizer.isNextTokenFloat()) {
// the spec(s) get(s) vague here. might be w, might be a color... chop it off.
tokenizer.nextToken();
}
mesh.vertices.append(glm::vec3(x, y, z));
} else if (token == "vn") {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
float x = std::stof(tokenizer.getDatum().data());
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
@ -143,35 +178,74 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeom
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
break;
}
// the spec gets vague here. might be w, might be a color... chop it off.
tokenizer.skipLine();
mesh.vertices.append(glm::vec3(x, y, z));
mesh.colors.append(glm::vec3(1, 1, 1));
float z = std::stof(tokenizer.getDatum().data());
while (tokenizer.isNextTokenFloat()) {
// the spec gets vague here. might be w
tokenizer.nextToken();
}
faceNormals.append(glm::vec3(x, y, z));
} else if (token == "f") {
// a face can have 3 or more vertices
QVector<int> indices;
QVector<int> normalIndices;
while (true) {
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) { goto done; }
try {
int vertexIndex = std::stoi(tokenizer.getDatum().data());
// negative indexes count backward from the current end of the vertex list
vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1);
// obj index is 1 based
assert(vertexIndex >= 1);
indices.append(vertexIndex - 1);
if (tokenizer.nextToken() != OBJTokenizer::DATUM_TOKEN) {
if (indices.count() == 0) {
goto done;
}
break;
}
catch(const std::exception& e) {
// wasn't a number, but it back.
// faces can be:
// vertex-index
// vertex-index/texture-index
// vertex-index/texture-index/surface-normal-index
QByteArray token = tokenizer.getDatum();
QList<QByteArray> parts = token.split('/');
assert(parts.count() >= 1);
assert(parts.count() <= 3);
QByteArray vertIndexBA = parts[ 0 ];
bool ok;
int vertexIndex = vertIndexBA.toInt(&ok);
if (!ok) {
// it wasn't #/#/#, put it back and exit this loop.
tokenizer.pushBackToken(OBJTokenizer::DATUM_TOKEN);
break;
}
// if (parts.count() > 1) {
// QByteArray textureIndexBA = parts[ 1 ];
// }
if (parts.count() > 2) {
QByteArray normalIndexBA = parts[ 2 ];
bool ok;
int normalIndex = normalIndexBA.toInt(&ok);
if (ok) {
normalIndices.append(normalIndex - 1);
}
}
// negative indexes count backward from the current end of the vertex list
vertexIndex = (vertexIndex >= 0 ? vertexIndex : mesh.vertices.count() + vertexIndex + 1);
// obj index is 1 based
assert(vertexIndex >= 1);
indices.append(vertexIndex - 1);
}
if (indices.count() == 3) {
// flip these around (because of the y/z swap above) so our triangles face outward
meshPart.triangleIndices.append(indices[0]);
meshPart.triangleIndices.append(indices[2]);
meshPart.triangleIndices.append(indices[1]);
meshPart.triangleIndices.append(indices[2]);
if (normalIndices.count() == 3) {
faceNormalIndexes.append(normalIndices[0]);
faceNormalIndexes.append(normalIndices[1]);
faceNormalIndexes.append(normalIndices[2]);
} else {
// hmm.
}
} else if (indices.count() == 4) {
meshPart.quadIndices << indices;
} else {
@ -205,6 +279,10 @@ FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping) {
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
FBXGeometry geometry;
OBJTokenizer tokenizer(device);
QVector<int> faceNormalIndexes;
QVector<glm::vec3> faceNormals;
faceNormalIndexes.clear();
geometry.meshExtents.reset();
geometry.meshes.append(FBXMesh());
@ -216,7 +294,7 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
// add a new meshPart to the geometry's single mesh.
bool success = true;
while (success) {
success = parseOBJGroup(tokenizer, mapping, geometry);
success = parseOBJGroup(tokenizer, mapping, geometry, faceNormals, faceNormalIndexes);
}
FBXMesh &mesh = geometry.meshes[0];
@ -229,31 +307,34 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
geometry.joints.resize(1);
geometry.joints[0].isFree = false;
// geometry.joints[0].freeLineage;
geometry.joints[0].parentIndex = -1;
geometry.joints[0].distanceToParent = 0;
geometry.joints[0].boneRadius = 0;
geometry.joints[0].translation = glm::vec3(0, 0, 0);
// geometry.joints[0].preTransform = ;
geometry.joints[0].preRotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].rotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].postRotation = glm::quat(0, 0, 0, 1);
// geometry.joints[0].postTransform = ;
// geometry.joints[0].transform = ;
geometry.joints[0].rotationMin = glm::vec3(0, 0, 0);
geometry.joints[0].rotationMax = glm::vec3(0, 0, 0);
geometry.joints[0].inverseDefaultRotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].inverseBindRotation = glm::quat(0, 0, 0, 1);
// geometry.joints[0].bindTransform = ;
geometry.joints[0].name = "OBJ";
geometry.joints[0].shapePosition = glm::vec3(0, 0, 0);
geometry.joints[0].shapeRotation = glm::quat(0, 0, 0, 1);
geometry.joints[0].shapeType = SPHERE_SHAPE;
geometry.joints[0].isSkeletonJoint = false;
geometry.joints[0].isSkeletonJoint = true;
geometry.jointIndices["x"] = 1;
FBXCluster cluster;
cluster.jointIndex = 0;
cluster.inverseBindMatrix = glm::mat4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1);
mesh.clusters.append(cluster);
// The OBJ format has normals for faces. The FBXGeometry structure has normals for points.
// run through all the faces, look-up (or determine) a normal and set the normal for the points
// that make up each face.
QVector<glm::vec3> pointNormalsSums;
// add bogus normal data for this mesh
mesh.normals.fill(glm::vec3(0,0,0), mesh.vertices.count());
mesh.tangents.fill(glm::vec3(0,0,0), mesh.vertices.count());
pointNormalsSums.fill(glm::vec3(0,0,0), mesh.vertices.count());
foreach (FBXMeshPart meshPart, mesh.parts) {
int triCount = meshPart.triangleIndices.count() / 3;
@ -262,21 +343,43 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
int p1Index = meshPart.triangleIndices[i*3+1];
int p2Index = meshPart.triangleIndices[i*3+2];
glm::vec3 p0 = mesh.vertices[p0Index];
glm::vec3 p1 = mesh.vertices[p1Index];
glm::vec3 p2 = mesh.vertices[p2Index];
assert(p0Index < mesh.vertices.count());
assert(p1Index < mesh.vertices.count());
assert(p2Index < mesh.vertices.count());
glm::vec3 n = glm::cross(p1 - p0, p2 - p0);
glm::vec3 t = glm::cross(p2 - p0, n);
glm::vec3 n0, n1, n2;
if (i < faceNormalIndexes.count()) {
int n0Index = faceNormalIndexes[i*3];
int n1Index = faceNormalIndexes[i*3+1];
int n2Index = faceNormalIndexes[i*3+2];
n0 = faceNormals[n0Index];
n1 = faceNormals[n1Index];
n2 = faceNormals[n2Index];
} else {
// We didn't read normals, add bogus normal data for this face
glm::vec3 p0 = mesh.vertices[p0Index];
glm::vec3 p1 = mesh.vertices[p1Index];
glm::vec3 p2 = mesh.vertices[p2Index];
n0 = glm::cross(p1 - p0, p2 - p0);
n1 = n0;
n2 = n0;
}
mesh.normals[p0Index] = n;
mesh.normals[p1Index] = n;
mesh.normals[p2Index] = n;
mesh.tangents[p0Index] = t;
mesh.tangents[p1Index] = t;
mesh.tangents[p2Index] = t;
// we sum up the normal for each point and then divide by the count to get an average
pointNormalsSums[p0Index] += n0;
pointNormalsSums[p1Index] += n1;
pointNormalsSums[p2Index] += n2;
}
int vertCount = mesh.vertices.count();
for (int i = 0; i < vertCount; i++) {
float length = glm::length(pointNormalsSums[i]);
if (length > FLT_EPSILON) {
mesh.normals[i] = glm::normalize(pointNormalsSums[i]);
}
}
// XXX do same normal calculation for quadCount
}
}
catch(const std::exception& e) {
@ -285,3 +388,77 @@ FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping) {
return geometry;
}
void fbxDebugDump(const FBXGeometry& fbxgeo) {
qDebug() << "---------------- fbxGeometry ----------------";
qDebug() << " hasSkeletonJoints =" << fbxgeo.hasSkeletonJoints;
qDebug() << " offset =" << fbxgeo.offset;
qDebug() << " attachments.count() = " << fbxgeo.attachments.count();
qDebug() << " meshes.count() =" << fbxgeo.meshes.count();
foreach (FBXMesh mesh, fbxgeo.meshes) {
qDebug() << " vertices.count() =" << mesh.vertices.count();
qDebug() << " normals.count() =" << mesh.normals.count();
if (mesh.normals.count() == mesh.vertices.count()) {
for (int i = 0; i < mesh.normals.count(); i++) {
qDebug() << " " << mesh.vertices[ i ] << mesh.normals[ i ];
}
}
qDebug() << " tangents.count() =" << mesh.tangents.count();
qDebug() << " colors.count() =" << mesh.colors.count();
qDebug() << " texCoords.count() =" << mesh.texCoords.count();
qDebug() << " texCoords1.count() =" << mesh.texCoords1.count();
qDebug() << " clusterIndices.count() =" << mesh.clusterIndices.count();
qDebug() << " clusterWeights.count() =" << mesh.clusterWeights.count();
qDebug() << " meshExtents =" << mesh.meshExtents;
qDebug() << " modelTransform =" << mesh.modelTransform;
qDebug() << " parts.count() =" << mesh.parts.count();
foreach (FBXMeshPart meshPart, mesh.parts) {
qDebug() << " quadIndices.count() =" << meshPart.quadIndices.count();
qDebug() << " triangleIndices.count() =" << meshPart.triangleIndices.count();
qDebug() << " diffuseColor =" << meshPart.diffuseColor;
qDebug() << " specularColor =" << meshPart.specularColor;
qDebug() << " emissiveColor =" << meshPart.emissiveColor;
qDebug() << " emissiveParams =" << meshPart.emissiveParams;
qDebug() << " shininess =" << meshPart.shininess;
qDebug() << " opacity =" << meshPart.opacity;
qDebug() << " materialID =" << meshPart.materialID;
}
qDebug() << " clusters.count() =" << mesh.clusters.count();
foreach (FBXCluster cluster, mesh.clusters) {
qDebug() << " jointIndex =" << cluster.jointIndex;
qDebug() << " inverseBindMatrix =" << cluster.inverseBindMatrix;
}
}
qDebug() << " jointIndices =" << fbxgeo.jointIndices;
qDebug() << " joints.count() =" << fbxgeo.joints.count();
foreach (FBXJoint joint, fbxgeo.joints) {
qDebug() << " isFree =" << joint.isFree;
qDebug() << " freeLineage" << joint.freeLineage;
qDebug() << " parentIndex" << joint.parentIndex;
qDebug() << " distanceToParent" << joint.distanceToParent;
qDebug() << " boneRadius" << joint.boneRadius;
qDebug() << " translation" << joint.translation;
qDebug() << " preTransform" << joint.preTransform;
qDebug() << " preRotation" << joint.preRotation;
qDebug() << " rotation" << joint.rotation;
qDebug() << " postRotation" << joint.postRotation;
qDebug() << " postTransform" << joint.postTransform;
qDebug() << " transform" << joint.transform;
qDebug() << " rotationMin" << joint.rotationMin;
qDebug() << " rotationMax" << joint.rotationMax;
qDebug() << " inverseDefaultRotation" << joint.inverseDefaultRotation;
qDebug() << " inverseBindRotation" << joint.inverseBindRotation;
qDebug() << " bindTransform" << joint.bindTransform;
qDebug() << " name" << joint.name;
qDebug() << " shapePosition" << joint.shapePosition;
qDebug() << " shapeRotation" << joint.shapeRotation;
qDebug() << " shapeType" << joint.shapeType;
qDebug() << " isSkeletonJoint" << joint.isSkeletonJoint;
}
qDebug() << "\n";
}

View file

@ -4,3 +4,4 @@
FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping);
FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping);
void fbxDebugDump(const FBXGeometry& fbxgeo);

View file

@ -25,14 +25,14 @@
#if defined(NSIGHT_FOUND)
#include "nvToolsExt.h"
class ProfileRange {
public:
ProfileRange(const char *name) {
nvtxRangePush(name);
}
~ProfileRange() {
nvtxRangePop();
}
class ProfileRange {
public:
ProfileRange(const char *name) {
nvtxRangePush(name);
}
~ProfileRange() {
nvtxRangePop();
}
};
#define PROFILE_RANGE(name) ProfileRange profileRangeThis(name);
@ -114,17 +114,17 @@ public:
// For now, instead of calling the raw glCall, use the equivalent call on the batch so the call is beeing recorded
// THe implementation of these functions is in GLBackend.cpp
void _glEnable(GLenum cap);
void _glDisable(GLenum cap);
void _glEnable(GLenum cap);
void _glDisable(GLenum cap);
void _glEnableClientState(GLenum array);
void _glDisableClientState(GLenum array);
void _glCullFace(GLenum mode);
void _glAlphaFunc(GLenum func, GLclampf ref);
void _glDepthFunc(GLenum func);
void _glDepthMask(GLboolean flag);
void _glDepthFunc(GLenum func);
void _glDepthMask(GLboolean flag);
void _glDepthRange(GLclampd zNear, GLclampd zFar);
void _glBindBuffer(GLenum target, GLuint buffer);
@ -138,14 +138,14 @@ public:
void _glUniform1f(GLint location, GLfloat v0);
void _glUniform2f(GLint location, GLfloat v0, GLfloat v1);
void _glUniform4fv(GLint location, GLsizei count, const GLfloat* value);
void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void _glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value);
void _glDrawArrays(GLenum mode, GLint first, GLsizei count);
void _glDrawArrays(GLenum mode, GLint first, GLsizei count);
void _glDrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const void *indices);
void _glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void _glNormalPointer(GLenum type, GLsizei stride, const void *pointer);
void _glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void _glColorPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void _glNormalPointer(GLenum type, GLsizei stride, const void *pointer);
void _glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void _glVertexPointer(GLint size, GLenum type, GLsizei stride, const void *pointer);
void _glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer);
@ -175,44 +175,44 @@ public:
// TODO: As long as we have gl calls explicitely issued from interface
// code, we need to be able to record and batch these calls. THe long
// term strategy is to get rid of any GL calls in favor of the HIFI GPU API
COMMAND_glEnable,
COMMAND_glDisable,
COMMAND_glEnableClientState,
COMMAND_glDisableClientState,
COMMAND_glCullFace,
COMMAND_glAlphaFunc,
COMMAND_glDepthFunc,
COMMAND_glDepthMask,
COMMAND_glDepthRange,
COMMAND_glBindBuffer,
COMMAND_glBindTexture,
COMMAND_glActiveTexture,
COMMAND_glDrawBuffers,
COMMAND_glUseProgram,
COMMAND_glUniform1f,
COMMAND_glUniform2f,
COMMAND_glUniform4fv,
COMMAND_glUniformMatrix4fv,
COMMAND_glDrawArrays,
COMMAND_glDrawRangeElements,
COMMAND_glColorPointer,
COMMAND_glNormalPointer,
COMMAND_glTexCoordPointer,
COMMAND_glVertexPointer,
COMMAND_glVertexAttribPointer,
COMMAND_glEnableVertexAttribArray,
COMMAND_glDisableVertexAttribArray,
COMMAND_glEnable,
COMMAND_glDisable,
COMMAND_glEnableClientState,
COMMAND_glDisableClientState,
COMMAND_glCullFace,
COMMAND_glAlphaFunc,
COMMAND_glDepthFunc,
COMMAND_glDepthMask,
COMMAND_glDepthRange,
COMMAND_glBindBuffer,
COMMAND_glBindTexture,
COMMAND_glActiveTexture,
COMMAND_glDrawBuffers,
COMMAND_glUseProgram,
COMMAND_glUniform1f,
COMMAND_glUniform2f,
COMMAND_glUniform4fv,
COMMAND_glUniformMatrix4fv,
COMMAND_glDrawArrays,
COMMAND_glDrawRangeElements,
COMMAND_glColorPointer,
COMMAND_glNormalPointer,
COMMAND_glTexCoordPointer,
COMMAND_glVertexPointer,
COMMAND_glVertexAttribPointer,
COMMAND_glEnableVertexAttribArray,
COMMAND_glDisableVertexAttribArray,
COMMAND_glColor4f,
NUM_COMMANDS,

View file

@ -50,15 +50,13 @@ bool writeOBJ(QString outFileName, QVector<QVector<VHACD::IVHACD::ConvexHull>>&
for (unsigned int i = 0; i < hull.m_nPoints; i++) {
out << "v ";
out << formatFloat(hull.m_points[i*3]) << " ";
// swap y and z because up is 3rd value in OBJ
out << formatFloat(hull.m_points[i*3+2]) << " ";
out << formatFloat(hull.m_points[i*3+1]) << "\n";
out << formatFloat(hull.m_points[i*3+1]) << " ";
out << formatFloat(hull.m_points[i*3+2]) << "\n";
}
for (unsigned int i = 0; i < hull.m_nTriangles; i++) {
out << "f ";
// change order to flip normal (due to swapping y and z, above)
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
out << hull.m_triangles[i*3] + 1 + pointStartOffset << " ";
out << hull.m_triangles[i*3+1] + 1 + pointStartOffset << " ";
out << hull.m_triangles[i*3+2] + 1 + pointStartOffset << "\n";
}
out << "\n";