History

Merge branch 'master' of https://github.com/highfidelity/hifi into removePropertiesFromRayPick

ZappoMan committed 9 years ago

Commit 246f3772 parents 9e287d4 73227f6364 files · +14004 −9659Browse files

Showing the changes to libraries/entities/src/EntityScriptingInterface.cpp (1 of 364 files). Show all

Unified · Split

libraries/entities/src/EntityScriptingInterface.cpp+163−13
@@ -8,8 +8,15 @@ · 7 unmodified lines
88 // Distributed under the Apache License, Version 2.0.
99 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
1010 //
11+
12+#include <glm/gtc/matrix_transform.hpp>
13+#include <glm/gtx/transform.hpp>
14+
1115 #include "EntityScriptingInterface.h"
1216
17+#include <QFutureWatcher>
18+#include <QtConcurrent/QtConcurrentRun>
19+
1320 #include "EntityItemID.h"
1421 #include <VariantMapToScriptValue.h>
1522 #include <SharedUtil.h>
@@ -289,13 +296,11 @@ EntityItemProperties EntityScriptingInterface::getEntityProperties(QUuid identit · 273 unmodified lines
289296
290297 results = entity->getProperties(desiredProperties);
291298
292− // TODO: improve sitting points and naturalDimensions in the future,
293− // for now we've included the old sitting points model behavior for entity types that are models
294− // we've also added this hack for setting natural dimensions of models
299+ // TODO: improve naturalDimensions in the future,
300+ // for now we've added this hack for setting natural dimensions of models
295301 if (entity->getType() == EntityTypes::Model) {
296302 const FBXGeometry* geometry = _entityTree->getGeometryForEntity(entity);
297303 if (geometry) {
298− results.setSittingPoints(geometry->sittingPoints);
299304 Extents meshExtents = geometry->getUnscaledMeshExtents();
300305 results.setNaturalDimensions(meshExtents.maximum - meshExtents.minimum);
301306 results.calculateNaturalPosition(meshExtents.minimum, meshExtents.maximum);
@@ -679,6 +684,118 @@ bool EntityScriptingInterface::reloadServerScripts(QUuid entityID) { · 377 unmodified lines
679684 return client->reloadServerScript(entityID);
680685 }
681686
687+bool EntityPropertyMetadataRequest::script(EntityItemID entityID, QScriptValue handler) {
688+ using LocalScriptStatusRequest = QFutureWatcher<QVariant>;
689+
690+ LocalScriptStatusRequest* request = new LocalScriptStatusRequest;
691+ QObject::connect(request, &LocalScriptStatusRequest::finished, _engine, [=]() mutable {
692+ auto details = request->result().toMap();
693+ QScriptValue err, result;
694+ if (details.contains("isError")) {
695+ if (!details.contains("message")) {
696+ details["message"] = details["errorInfo"];
697+ }
698+ err = _engine->makeError(_engine->toScriptValue(details));
699+ } else {
700+ details["success"] = true;
701+ result = _engine->toScriptValue(details);
702+ }
703+ callScopedHandlerObject(handler, err, result);
704+ request->deleteLater();
705+ });
706+ auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
707+ entityScriptingInterface->withEntitiesScriptEngine([&](EntitiesScriptEngineProvider* entitiesScriptEngine) {
708+ if (entitiesScriptEngine) {
709+ request->setFuture(entitiesScriptEngine->getLocalEntityScriptDetails(entityID));
710+ }
711+ });
712+ if (!request->isStarted()) {
713+ request->deleteLater();
714+ callScopedHandlerObject(handler, _engine->makeError("Entities Scripting Provider unavailable", "InternalError"), QScriptValue());
715+ return false;
716+ }
717+ return true;
718+}
719+
720+bool EntityPropertyMetadataRequest::serverScripts(EntityItemID entityID, QScriptValue handler) {
721+ auto client = DependencyManager::get<EntityScriptClient>();
722+ auto request = client->createScriptStatusRequest(entityID);
723+ QPointer<BaseScriptEngine> engine = _engine;
724+ QObject::connect(request, &GetScriptStatusRequest::finished, _engine, [=](GetScriptStatusRequest* request) mutable {
725+ auto engine = _engine;
726+ if (!engine) {
727+ qCDebug(entities) << __FUNCTION__ << " -- engine destroyed while inflight" << entityID;
728+ return;
729+ }
730+ QVariantMap details;
731+ details["success"] = request->getResponseReceived();
732+ details["isRunning"] = request->getIsRunning();
733+ details["status"] = EntityScriptStatus_::valueToKey(request->getStatus()).toLower();
734+ details["errorInfo"] = request->getErrorInfo();
735+
736+ QScriptValue err, result;
737+ if (!details["success"].toBool()) {
738+ if (!details.contains("message") && details.contains("errorInfo")) {
739+ details["message"] = details["errorInfo"];
740+ }
741+ if (details["message"].toString().isEmpty()) {
742+ details["message"] = "entity server script details not found";
743+ }
744+ err = engine->makeError(engine->toScriptValue(details));
745+ } else {
746+ result = engine->toScriptValue(details);
747+ }
748+ callScopedHandlerObject(handler, err, result);
749+ request->deleteLater();
750+ });
751+ request->start();
752+ return true;
753+}
754+
755+bool EntityScriptingInterface::queryPropertyMetadata(QUuid entityID, QScriptValue property, QScriptValue scopeOrCallback, QScriptValue methodOrName) {
756+ auto name = property.toString();
757+ auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName);
758+ QPointer<BaseScriptEngine> engine = dynamic_cast<BaseScriptEngine*>(handler.engine());
759+ if (!engine) {
760+ qCDebug(entities) << "queryPropertyMetadata without detectable engine" << entityID << name;
761+ return false;
762+ }
763+#ifdef DEBUG_ENGINE_STATE
764+ connect(engine, &QObject::destroyed, this, [=]() {
765+ qDebug() << "queryPropertyMetadata -- engine destroyed!" << (!engine ? "nullptr" : "engine");
766+ });
767+#endif
768+ if (!handler.property("callback").isFunction()) {
769+ qDebug() << "!handler.callback.isFunction" << engine;
770+ engine->raiseException(engine->makeError("callback is not a function", "TypeError"));
771+ return false;
772+ }
773+
774+ // NOTE: this approach is a work-in-progress and for now just meant to work 100% correctly and provide
775+ // some initial structure for organizing metadata adapters around.
776+
777+ // The extra layer of indirection is *essential* because in real world conditions errors are often introduced
778+ // by accident and sometimes without exact memory of "what just changed."
779+
780+ // Here the scripter only needs to know an entityID and a property name -- which means all scripters can
781+ // level this method when stuck in dead-end scenarios or to learn more about "magic" Entity properties
782+ // like .script that work in terms of side-effects.
783+
784+ // This is an async callback pattern -- so if needed C++ can easily throttle or restrict queries later.
785+
786+ EntityPropertyMetadataRequest request(engine);
787+
788+ if (name == "script") {
789+ return request.script(entityID, handler);
790+ } else if (name == "serverScripts") {
791+ return request.serverScripts(entityID, handler);
792+ } else {
793+ engine->raiseException(engine->makeError("metadata for property " + name + " is not yet queryable"));
794+ engine->maybeEmitUncaughtException(__FUNCTION__);
795+ return false;
796+ }
797+}
798+
682799 bool EntityScriptingInterface::getServerScriptStatus(QUuid entityID, QScriptValue callback) {
683800 auto client = DependencyManager::get<EntityScriptClient>();
684801 auto request = client->createScriptStatusRequest(entityID);
@@ -806,8 +923,7 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra · 121 unmodified lines
806923 }
807924 }
808925
809−bool EntityScriptingInterface::setVoxels(QUuid entityID,
810− std::function<bool(PolyVoxEntityItem&)> actor) {
926+bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor) {
811927 PROFILE_RANGE(script_entities, __FUNCTION__);
812928
813929 if (!_entityTree) {
@@ -873,11 +989,9 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(Line · 59 unmodified lines
873989 return success;
874990 }
875991
876−
877992 bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
878993 PROFILE_RANGE(script_entities, __FUNCTION__);
879−
880− return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
994+ return polyVoxWorker(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
881995 return polyVoxEntity.setSphere(center, radius, value);
882996 });
883997 }
@@ -887,7 +1001,7 @@ bool EntityScriptingInterface::setVoxelCapsule(QUuid entityID, · 3 unmodified lines
8871001 float radius, int value) {
8881002 PROFILE_RANGE(script_entities, __FUNCTION__);
8891003
890− return setVoxels(entityID, [start, end, radius, value](PolyVoxEntityItem& polyVoxEntity) {
1004+ return polyVoxWorker(entityID, [start, end, radius, value](PolyVoxEntityItem& polyVoxEntity) {
8911005 return polyVoxEntity.setCapsule(start, end, radius, value);
8921006 });
8931007 }
@@ -895,7 +1009,7 @@ bool EntityScriptingInterface::setVoxelCapsule(QUuid entityID, · 1 unmodified line
8951009 bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) {
8961010 PROFILE_RANGE(script_entities, __FUNCTION__);
8971011
898− return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
1012+ return polyVoxWorker(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
8991013 return polyVoxEntity.setVoxelInVolume(position, value);
9001014 });
9011015 }
@@ -903,7 +1017,7 @@ bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& positio · 1 unmodified line
9031017 bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
9041018 PROFILE_RANGE(script_entities, __FUNCTION__);
9051019
906− return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
1020+ return polyVoxWorker(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
9071021 return polyVoxEntity.setAll(value);
9081022 });
9091023 }
@@ -912,11 +1026,30 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3 · 2 unmodified lines
9121026 const glm::vec3& cuboidSize, int value) {
9131027 PROFILE_RANGE(script_entities, __FUNCTION__);
9141028
915− return setVoxels(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) {
1029+ return polyVoxWorker(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) {
9161030 return polyVoxEntity.setCuboid(lowPosition, cuboidSize, value);
9171031 });
9181032 }
9191033
1034+void EntityScriptingInterface::voxelsToMesh(QUuid entityID, QScriptValue callback) {
1035+ PROFILE_RANGE(script_entities, __FUNCTION__);
1036+
1037+ bool success { false };
1038+ QScriptValue mesh { false };
1039+
1040+ polyVoxWorker(entityID, [&](PolyVoxEntityItem& polyVoxEntity) mutable {
1041+ if (polyVoxEntity.getOnCount() == 0) {
1042+ success = true;
1043+ } else {
1044+ success = polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh);
1045+ }
1046+ return true;
1047+ });
1048+
1049+ QScriptValueList args { mesh, success };
1050+ callback.call(QScriptValue(), args);
1051+}
1052+
9201053 bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::vec3>& points) {
9211054 PROFILE_RANGE(script_entities, __FUNCTION__);
9221055
@@ -1532,3 +1665,20 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons · 609 unmodified lines
15321665 AABox aaBox(low, dimensions);
15331666 return aaBox.findCapsulePenetration(start, end, radius, penetration);
15341667 }
1668+
1669+glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) {
1670+ glm::mat4 result;
1671+ if (_entityTree) {
1672+ _entityTree->withReadLock([&] {
1673+ EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
1674+ if (entity) {
1675+ glm::mat4 translation = glm::translate(entity->getPosition());
1676+ glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
1677+ glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT -
1678+ entity->getRegistrationPoint());
1679+ result = translation * rotation * registration;
1680+ }
1681+ });
1682+ }
1683+ return result;
1684+}