//// EntityScriptingInterface.cpp// libraries/entities/src//// Created by Brad Hefta-Gaub on 12/6/13.// Copyright 2013 High Fidelity, Inc.//// Distributed under the Apache License, Version 2.0.// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include <VariantMapToScriptValue.h> #include "EntityScriptingInterface.h"#include "EntityTree.h"#include "LightEntityItem.h"#include "ModelEntityItem.h" EntityScriptingInterface::EntityScriptingInterface() : _nextCreatorTokenID(0), _entityTree(NULL){ auto nodeList = DependencyManager::get<NodeList>(); connect(nodeList.data(), &NodeList::canAdjustLocksChanged, this, &EntityScriptingInterface::canAdjustLocksChanged); connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);} void EntityScriptingInterface::queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties) { getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties);} bool EntityScriptingInterface::canAdjustLocks() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanAdjustLocks();} bool EntityScriptingInterface::canRez() { auto nodeList = DependencyManager::get<NodeList>(); return nodeList->getThisNodeCanRez();} void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) { if (_entityTree) { disconnect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); disconnect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity); disconnect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID); disconnect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities); } _entityTree = modelTree; if (_entityTree) { connect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity); connect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity); connect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID); connect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities); }} void setSimId(EntityItemProperties& propertiesWithSimID, EntityItem* entity) { auto nodeList = DependencyManager::get<NodeList>(); const QUuid myNodeID = nodeList->getSessionUUID(); // if this entity has non-zero physics/simulation related values, claim simulation ownership if (propertiesWithSimID.velocityChanged() || propertiesWithSimID.rotationChanged() || propertiesWithSimID.containsPositionChange()) { propertiesWithSimID.setSimulatorID(myNodeID); entity->setSimulatorID(myNodeID); } else if (entity->getSimulatorID() == myNodeID) { propertiesWithSimID.setSimulatorID(QUuid()); // give up simulation ownership entity->setSimulatorID(QUuid()); }} EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) { // The application will keep track of creatorTokenID uint32_t creatorTokenID = EntityItemID::getNextCreatorTokenID(); EntityItemProperties propertiesWithSimID = properties; EntityItemID id(NEW_ENTITY, creatorTokenID, false ); // If we have a local entity tree set, then also update it. if (_entityTree) { _entityTree->lockForWrite(); EntityItem* entity = _entityTree->addEntity(id, propertiesWithSimID); // This Node is creating a new object. If it's in motion, set this Node as the simulator. setSimId(propertiesWithSimID, entity); _entityTree->unlock(); } // queue the packet queueEntityMessage(PacketTypeEntityAddOrEdit, id, propertiesWithSimID); return id;} EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) { EntityItemID actualID = entityID; if (!entityID.isKnownID) { actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); if (actualID == UNKNOWN_ENTITY_ID) { return entityID; // bailing early } // found it! entityID.id = actualID.id; entityID.isKnownID = true; } return entityID;} EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID entityID) { EntityItemProperties results; EntityItemID identity = identifyEntity(entityID); if (_entityTree) { _entityTree->lockForRead(); EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity)); if (entity) { results = entity->getProperties(); // TODO: improve sitting points and naturalDimensions in the future, // for now we've included the old sitting points model behavior for entity types that are models // we've also added this hack for setting natural dimensions of models if (entity->getType() == EntityTypes::Model) { const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity); if (geometry) { results.setSittingPoints(geometry->sittingPoints); Extents meshExtents = geometry->getUnscaledMeshExtents(); results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum); } } } else { results.setIsUnknownID(); } _entityTree->unlock(); } return results;} EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const EntityItemProperties& properties) { EntityItemID actualID = entityID; // if the entity is unknown, attempt to look it up if (!entityID.isKnownID) { actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); if (actualID.id != UNKNOWN_ENTITY_ID) { entityID.id = actualID.id; entityID.isKnownID = true; } } EntityItemProperties propertiesWithSimID = properties; // If we have a local entity tree set, then also update it. We can do this even if we don't know // the actual id, because we can edit out local entities just with creatorTokenID if (_entityTree) { _entityTree->lockForWrite(); _entityTree->updateEntity(entityID, propertiesWithSimID, canAdjustLocks()); _entityTree->unlock(); } // if at this point, we know the id, send the update to the entity server if (entityID.isKnownID) { // make sure the properties has a type, so that the encode can know which properties to include if (propertiesWithSimID.getType() == EntityTypes::Unknown) { EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID); if (entity) { propertiesWithSimID.setType(entity->getType()); setSimId(propertiesWithSimID, entity); } } queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, propertiesWithSimID); } return entityID;} void EntityScriptingInterface::deleteEntity(EntityItemID entityID) { EntityItemID actualID = entityID; // if the entity is unknown, attempt to look it up if (!entityID.isKnownID) { actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID); if (actualID.id != UNKNOWN_ENTITY_ID) { entityID.id = actualID.id; entityID.isKnownID = true; } } bool shouldDelete = true; // If we have a local entity tree set, then also update it. if (_entityTree) { _entityTree->lockForWrite(); EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(actualID)); if (entity) { if (entity->getLocked()) { shouldDelete = false; } else { _entityTree->deleteEntity(entityID); } } _entityTree->unlock(); } // if at this point, we know the id, and we should still delete the entity, send the update to the entity server if (shouldDelete && entityID.isKnownID) { getEntityPacketSender()->queueEraseEntityMessage(entityID); }} EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const { EntityItemID result(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false); if (_entityTree) { _entityTree->lockForRead(); const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius); _entityTree->unlock(); if (closestEntity) { result = closestEntity->getEntityItemID(); } } return result;} void EntityScriptingInterface::dumpTree() const { if (_entityTree) { _entityTree->lockForRead(); _entityTree->dumpTree(); _entityTree->unlock(); }} QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const { QVector<EntityItemID> result; if (_entityTree) { _entityTree->lockForRead(); QVector<const EntityItem*> entities; _entityTree->findEntities(center, radius, entities); _entityTree->unlock(); foreach (const EntityItem* entity, entities) { 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;} RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking) { return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);} RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking) { return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);} RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray, Octree::lockType lockType, bool precisionPicking) { RayToEntityIntersectionResult result; if (_entityTree) { OctreeElement* element; EntityItem* intersectedEntity = NULL; result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face, (void**)&intersectedEntity, lockType, &result.accurate, precisionPicking); if (result.intersects && intersectedEntity) { result.entityID = intersectedEntity->getEntityItemID(); result.properties = intersectedEntity->getProperties(); result.intersection = ray.origin + (ray.direction * result.distance); } } return result;} void EntityScriptingInterface::setLightsArePickable(bool value) { LightEntityItem::setLightsArePickable(value);} bool EntityScriptingInterface::getLightsArePickable() const { return LightEntityItem::getLightsArePickable();} void EntityScriptingInterface::setSendPhysicsUpdates(bool value) { EntityItem::setSendPhysicsUpdates(value);} bool EntityScriptingInterface::getSendPhysicsUpdates() const { return EntityItem::getSendPhysicsUpdates();} RayToEntityIntersectionResult::RayToEntityIntersectionResult() : intersects(false), accurate(true), // assume it's accurate entityID(), properties(), distance(0), face(), entity(NULL){ } QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) { QScriptValue obj = engine->newObject(); obj.setProperty("intersects", value.intersects); obj.setProperty("accurate", value.accurate); QScriptValue entityItemValue = EntityItemIDtoScriptValue(engine, value.entityID); obj.setProperty("entityID", entityItemValue); QScriptValue propertiesValue = EntityItemPropertiesToScriptValue(engine, value.properties); obj.setProperty("properties", propertiesValue); obj.setProperty("distance", value.distance); QString faceName = ""; // handle BoxFace switch (value.face) { case MIN_X_FACE: faceName = "MIN_X_FACE"; break; case MAX_X_FACE: faceName = "MAX_X_FACE"; break; case MIN_Y_FACE: faceName = "MIN_Y_FACE"; break; case MAX_Y_FACE: faceName = "MAX_Y_FACE"; break; case MIN_Z_FACE: faceName = "MIN_Z_FACE"; break; case MAX_Z_FACE: faceName = "MAX_Z_FACE"; break; case UNKNOWN_FACE: faceName = "UNKNOWN_FACE"; break; } obj.setProperty("face", faceName); QScriptValue intersection = vec3toScriptValue(engine, value.intersection); obj.setProperty("intersection", intersection); return obj;} void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& value) { value.intersects = object.property("intersects").toVariant().toBool(); value.accurate = object.property("accurate").toVariant().toBool(); QScriptValue entityIDValue = object.property("entityID"); if (entityIDValue.isValid()) { EntityItemIDfromScriptValue(entityIDValue, value.entityID); } QScriptValue entityPropertiesValue = object.property("properties"); if (entityPropertiesValue.isValid()) { EntityItemPropertiesFromScriptValue(entityPropertiesValue, value.properties); } value.distance = object.property("distance").toVariant().toFloat(); QString faceName = object.property("face").toVariant().toString(); if (faceName == "MIN_X_FACE") { value.face = MIN_X_FACE; } else if (faceName == "MAX_X_FACE") { value.face = MAX_X_FACE; } else if (faceName == "MIN_Y_FACE") { value.face = MIN_Y_FACE; } else if (faceName == "MAX_Y_FACE") { value.face = MAX_Y_FACE; } else if (faceName == "MIN_Z_FACE") { value.face = MIN_Z_FACE; } else { value.face = MAX_Z_FACE; }; QScriptValue intersection = object.property("intersection"); if (intersection.isValid()) { vec3FromScriptValue(intersection, value.intersection); }}
//
// EntityScriptingInterface.cpp
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/6/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <VariantMapToScriptValue.h>
#include "EntityScriptingInterface.h"
#include "EntityTree.h"
#include "LightEntityItem.h"
#include "ModelEntityItem.h"
EntityScriptingInterface::EntityScriptingInterface() :
_nextCreatorTokenID(0),
_entityTree(NULL)
{
auto nodeList = DependencyManager::get<NodeList>();
connect(nodeList.data(), &NodeList::canAdjustLocksChanged, this, &EntityScriptingInterface::canAdjustLocksChanged);
connect(nodeList.data(), &NodeList::canRezChanged, this, &EntityScriptingInterface::canRezChanged);
}
void EntityScriptingInterface::queueEntityMessage(PacketType packetType,
EntityItemID entityID, const EntityItemProperties& properties) {
getEntityPacketSender()->queueEditEntityMessage(packetType, entityID, properties);
}
bool EntityScriptingInterface::canAdjustLocks() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanAdjustLocks();
}
bool EntityScriptingInterface::canRez() {
auto nodeList = DependencyManager::get<NodeList>();
return nodeList->getThisNodeCanRez();
}
void EntityScriptingInterface::setEntityTree(EntityTree* modelTree) {
if (_entityTree) {
disconnect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);
disconnect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);
disconnect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID);
disconnect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);
}
_entityTree = modelTree;
if (_entityTree) {
connect(_entityTree, &EntityTree::addingEntity, this, &EntityScriptingInterface::addingEntity);
connect(_entityTree, &EntityTree::deletingEntity, this, &EntityScriptingInterface::deletingEntity);
connect(_entityTree, &EntityTree::changingEntityID, this, &EntityScriptingInterface::changingEntityID);
connect(_entityTree, &EntityTree::clearingEntities, this, &EntityScriptingInterface::clearingEntities);
}
}
void setSimId(EntityItemProperties& propertiesWithSimID, EntityItem* entity) {
auto nodeList = DependencyManager::get<NodeList>();
const QUuid myNodeID = nodeList->getSessionUUID();
// if this entity has non-zero physics/simulation related values, claim simulation ownership
if (propertiesWithSimID.velocityChanged() ||
propertiesWithSimID.rotationChanged() ||
propertiesWithSimID.containsPositionChange()) {
propertiesWithSimID.setSimulatorID(myNodeID);
entity->setSimulatorID(myNodeID);
} else if (entity->getSimulatorID() == myNodeID) {
propertiesWithSimID.setSimulatorID(QUuid()); // give up simulation ownership
entity->setSimulatorID(QUuid());
}
}
EntityItemID EntityScriptingInterface::addEntity(const EntityItemProperties& properties) {
// The application will keep track of creatorTokenID
uint32_t creatorTokenID = EntityItemID::getNextCreatorTokenID();
EntityItemProperties propertiesWithSimID = properties;
EntityItemID id(NEW_ENTITY, creatorTokenID, false );
// If we have a local entity tree set, then also update it.
if (_entityTree) {
_entityTree->lockForWrite();
EntityItem* entity = _entityTree->addEntity(id, propertiesWithSimID);
// This Node is creating a new object. If it's in motion, set this Node as the simulator.
setSimId(propertiesWithSimID, entity);
_entityTree->unlock();
}
// queue the packet
queueEntityMessage(PacketTypeEntityAddOrEdit, id, propertiesWithSimID);
return id;
}
EntityItemID EntityScriptingInterface::identifyEntity(EntityItemID entityID) {
EntityItemID actualID = entityID;
if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
if (actualID == UNKNOWN_ENTITY_ID) {
return entityID; // bailing early
}
// found it!
entityID.id = actualID.id;
entityID.isKnownID = true;
}
return entityID;
}
EntityItemProperties EntityScriptingInterface::getEntityProperties(EntityItemID entityID) {
EntityItemProperties results;
EntityItemID identity = identifyEntity(entityID);
if (_entityTree) {
_entityTree->lockForRead();
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(identity));
if (entity) {
results = entity->getProperties();
// TODO: improve sitting points and naturalDimensions in the future,
// for now we've included the old sitting points model behavior for entity types that are models
// we've also added this hack for setting natural dimensions of models
if (entity->getType() == EntityTypes::Model) {
const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
if (geometry) {
results.setSittingPoints(geometry->sittingPoints);
Extents meshExtents = geometry->getUnscaledMeshExtents();
results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
}
}
} else {
results.setIsUnknownID();
}
_entityTree->unlock();
}
return results;
}
EntityItemID EntityScriptingInterface::editEntity(EntityItemID entityID, const EntityItemProperties& properties) {
EntityItemID actualID = entityID;
// if the entity is unknown, attempt to look it up
if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
entityID.isKnownID = true;
}
}
EntityItemProperties propertiesWithSimID = properties;
// If we have a local entity tree set, then also update it. We can do this even if we don't know
// the actual id, because we can edit out local entities just with creatorTokenID
if (_entityTree) {
_entityTree->lockForWrite();
_entityTree->updateEntity(entityID, propertiesWithSimID, canAdjustLocks());
_entityTree->unlock();
}
// if at this point, we know the id, send the update to the entity server
if (entityID.isKnownID) {
// make sure the properties has a type, so that the encode can know which properties to include
if (propertiesWithSimID.getType() == EntityTypes::Unknown) {
EntityItem* entity = _entityTree->findEntityByEntityItemID(entityID);
if (entity) {
propertiesWithSimID.setType(entity->getType());
setSimId(propertiesWithSimID, entity);
}
}
queueEntityMessage(PacketTypeEntityAddOrEdit, entityID, propertiesWithSimID);
}
return entityID;
}
void EntityScriptingInterface::deleteEntity(EntityItemID entityID) {
EntityItemID actualID = entityID;
// if the entity is unknown, attempt to look it up
if (!entityID.isKnownID) {
actualID = EntityItemID::getIDfromCreatorTokenID(entityID.creatorTokenID);
if (actualID.id != UNKNOWN_ENTITY_ID) {
entityID.id = actualID.id;
entityID.isKnownID = true;
}
}
bool shouldDelete = true;
// If we have a local entity tree set, then also update it.
if (_entityTree) {
_entityTree->lockForWrite();
EntityItem* entity = const_cast<EntityItem*>(_entityTree->findEntityByEntityItemID(actualID));
if (entity) {
if (entity->getLocked()) {
shouldDelete = false;
} else {
_entityTree->deleteEntity(entityID);
}
}
_entityTree->unlock();
}
// if at this point, we know the id, and we should still delete the entity, send the update to the entity server
if (shouldDelete && entityID.isKnownID) {
getEntityPacketSender()->queueEraseEntityMessage(entityID);
}
}
EntityItemID EntityScriptingInterface::findClosestEntity(const glm::vec3& center, float radius) const {
EntityItemID result(UNKNOWN_ENTITY_ID, UNKNOWN_ENTITY_TOKEN, false);
if (_entityTree) {
_entityTree->lockForRead();
const EntityItem* closestEntity = _entityTree->findClosestEntity(center, radius);
_entityTree->unlock();
if (closestEntity) {
result = closestEntity->getEntityItemID();
}
}
return result;
}
void EntityScriptingInterface::dumpTree() const {
if (_entityTree) {
_entityTree->lockForRead();
_entityTree->dumpTree();
_entityTree->unlock();
}
}
QVector<EntityItemID> EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const {
QVector<EntityItemID> result;
if (_entityTree) {
_entityTree->lockForRead();
QVector<const EntityItem*> entities;
_entityTree->findEntities(center, radius, entities);
_entityTree->unlock();
foreach (const EntityItem* entity, entities) {
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;
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersection(const PickRay& ray, bool precisionPicking) {
return findRayIntersectionWorker(ray, Octree::TryLock, precisionPicking);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionBlocking(const PickRay& ray, bool precisionPicking) {
return findRayIntersectionWorker(ray, Octree::Lock, precisionPicking);
}
RayToEntityIntersectionResult EntityScriptingInterface::findRayIntersectionWorker(const PickRay& ray,
Octree::lockType lockType,
bool precisionPicking) {
RayToEntityIntersectionResult result;
if (_entityTree) {
OctreeElement* element;
EntityItem* intersectedEntity = NULL;
result.intersects = _entityTree->findRayIntersection(ray.origin, ray.direction, element, result.distance, result.face,
(void**)&intersectedEntity, lockType, &result.accurate,
precisionPicking);
if (result.intersects && intersectedEntity) {
result.entityID = intersectedEntity->getEntityItemID();
result.properties = intersectedEntity->getProperties();
result.intersection = ray.origin + (ray.direction * result.distance);
}
}
return result;
}
void EntityScriptingInterface::setLightsArePickable(bool value) {
LightEntityItem::setLightsArePickable(value);
}
bool EntityScriptingInterface::getLightsArePickable() const {
return LightEntityItem::getLightsArePickable();
}
void EntityScriptingInterface::setSendPhysicsUpdates(bool value) {
EntityItem::setSendPhysicsUpdates(value);
}
bool EntityScriptingInterface::getSendPhysicsUpdates() const {
return EntityItem::getSendPhysicsUpdates();
}
RayToEntityIntersectionResult::RayToEntityIntersectionResult() :
intersects(false),
accurate(true), // assume it's accurate
entityID(),
properties(),
distance(0),
face(),
entity(NULL)
{
}
QScriptValue RayToEntityIntersectionResultToScriptValue(QScriptEngine* engine, const RayToEntityIntersectionResult& value) {
QScriptValue obj = engine->newObject();
obj.setProperty("intersects", value.intersects);
obj.setProperty("accurate", value.accurate);
QScriptValue entityItemValue = EntityItemIDtoScriptValue(engine, value.entityID);
obj.setProperty("entityID", entityItemValue);
QScriptValue propertiesValue = EntityItemPropertiesToScriptValue(engine, value.properties);
obj.setProperty("properties", propertiesValue);
obj.setProperty("distance", value.distance);
QString faceName = "";
// handle BoxFace
switch (value.face) {
case MIN_X_FACE:
faceName = "MIN_X_FACE";
break;
case MAX_X_FACE:
faceName = "MAX_X_FACE";
break;
case MIN_Y_FACE:
faceName = "MIN_Y_FACE";
break;
case MAX_Y_FACE:
faceName = "MAX_Y_FACE";
break;
case MIN_Z_FACE:
faceName = "MIN_Z_FACE";
break;
case MAX_Z_FACE:
faceName = "MAX_Z_FACE";
break;
case UNKNOWN_FACE:
faceName = "UNKNOWN_FACE";
break;
}
obj.setProperty("face", faceName);
QScriptValue intersection = vec3toScriptValue(engine, value.intersection);
obj.setProperty("intersection", intersection);
return obj;
}
void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, RayToEntityIntersectionResult& value) {
value.intersects = object.property("intersects").toVariant().toBool();
value.accurate = object.property("accurate").toVariant().toBool();
QScriptValue entityIDValue = object.property("entityID");
if (entityIDValue.isValid()) {
EntityItemIDfromScriptValue(entityIDValue, value.entityID);
}
QScriptValue entityPropertiesValue = object.property("properties");
if (entityPropertiesValue.isValid()) {
EntityItemPropertiesFromScriptValue(entityPropertiesValue, value.properties);
}
value.distance = object.property("distance").toVariant().toFloat();
QString faceName = object.property("face").toVariant().toString();
if (faceName == "MIN_X_FACE") {
value.face = MIN_X_FACE;
} else if (faceName == "MAX_X_FACE") {
value.face = MAX_X_FACE;
} else if (faceName == "MIN_Y_FACE") {
value.face = MIN_Y_FACE;
} else if (faceName == "MAX_Y_FACE") {
value.face = MAX_Y_FACE;
} else if (faceName == "MIN_Z_FACE") {
value.face = MIN_Z_FACE;
} else {
value.face = MAX_Z_FACE;
};
QScriptValue intersection = object.property("intersection");
if (intersection.isValid()) {
vec3FromScriptValue(intersection, value.intersection);
}
}