mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-29 22:43:03 +02:00
415 lines
15 KiB
C++
415 lines
15 KiB
C++
//
|
|
// 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);
|
|
qDebug() << "script claiming ownership";
|
|
} else if (entity->getSimulatorID() == myNodeID) {
|
|
propertiesWithSimID.setSimulatorID(QUuid()); // give up simulation ownership
|
|
entity->setSimulatorID(QUuid());
|
|
qDebug() << "script releasing ownership";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
}
|
|
}
|