[WL21389] Addresses physics library dependency and has some other fixes (details below).

* Addresses physics library dependency by moving computeShapeInfo override from
ShapeEntityItem (which is within Entities Library) to RenderableShapeEntityItem
(which is in Entities-Renderer Library).
** Entities-Renderer library already links against the physic library.
** Per discussion with Andrew Meadows: In order to ShapeEntityItem to be
utilized the library dependency between the Entity and Physics library
would need to be resolved to avoid the cyclical reliance which isn't in
the scope of this ticket.
* Updates shapeSpawner test script from the default clone of basicEntityTest\entitySpawner.js
** Objects now have a finite lifetime
** Script now cleans up the objects created when the script ends
** Also moved some adjustable properties out into var aliases at the top of the
file for easier/less error prone tweaking. Should probably add one for the shapeType.
* Fixes some issues with validateShapeType helper function
* Removed naive attempt at including physics library within entities library.
* Transferred some todos from notes
* Fixed some formatting

NOTE(s):
  This compiles and runs.  Cylinder is spawned and treated as CYLINDER_Y.

TODO(s):
* Add tweakable var for shapeType within shapeSpawner.js
* Vet and verify other shapes.
* Add in edge case handling.
* Add in support for other shapes to ShapeInfo infrastructure.

Changes to be committed:
	modified:   libraries/entities-renderer/src/RenderableShapeEntityItem.cpp
	modified:   libraries/entities-renderer/src/RenderableShapeEntityItem.h
	modified:   libraries/entities/CMakeLists.txt
	modified:   libraries/entities/src/ShapeEntityItem.cpp
	modified:   libraries/entities/src/ShapeEntityItem.h
	modified:   libraries/physics/src/ShapeFactory.cpp
	modified:   libraries/shared/src/ShapeInfo.cpp
	modified:   scripts/developer/tests/basicEntityTest/shapeSpawner.js
This commit is contained in:
LaShonda Hopper 2017-07-18 17:15:02 -04:00
parent 5bc38bd7f0
commit 75403124b6
8 changed files with 226 additions and 209 deletions

View file

@ -16,6 +16,7 @@
#include <StencilMaskPass.h>
#include <GeometryCache.h>
#include <PerfStat.h>
#include <ShapeFactory.h>
#include <render-utils/simple_vert.h>
#include <render-utils/simple_frag.h>
@ -48,11 +49,12 @@ RenderableShapeEntityItem::Pointer RenderableShapeEntityItem::baseFactory(const
}
EntityItemPointer RenderableShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
auto result = baseFactory(entityID, properties);
auto result = baseFactory(entityID, properties);
qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id;
//TODO_CUSACK: Remove this before final PN
qCDebug(entities) << "Creating RenderableShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id;
return result;
return result;
}
EntityItemPointer RenderableShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
@ -86,6 +88,23 @@ bool RenderableShapeEntityItem::isTransparent() {
}
}
void RenderableShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE) {
if (_shape == entity::Shape::NUM_SHAPES)
{
EntityItem::computeShapeInfo(info);
//--EARLY EXIT--( allow default handling to process )
return;
}
_collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions());
}
return EntityItem::computeShapeInfo(info);
}
void RenderableShapeEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderableShapeEntityItem::render");
//Q_ASSERT(getType() == EntityTypes::Shape);

View file

@ -28,6 +28,8 @@ public:
bool isTransparent() override;
void computeShapeInfo(ShapeInfo& info);
private:
std::unique_ptr<Procedural> _procedural { nullptr };

View file

@ -1,4 +1,4 @@
set(TARGET_NAME entities)
setup_hifi_library(Network Script)
link_hifi_libraries(shared networking octree avatars physics)
link_hifi_libraries(shared networking octree avatars)

View file

@ -12,7 +12,6 @@
#include <QtCore/QDebug>
#include <GeometryUtil.h>
#include <ShapeFactory.h>
#include "EntitiesLogging.h"
#include "EntityItemProperties.h"
@ -59,11 +58,12 @@ ShapeEntityItem::Pointer ShapeEntityItem::baseFactory(const EntityItemID& entity
}
EntityItemPointer ShapeEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
auto result = baseFactory(entityID, properties);
auto result = baseFactory(entityID, properties);
qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id;
//TODO_CUSACK: Remove this before final PN
qCDebug(entities) << "Creating ShapeEntityItem( " << result->_name << " ): " << result.get() << " ID: " << result->_id;
return result;
return result;
}
EntityItemPointer ShapeEntityItem::boxFactory(const EntityItemID& entityID, const EntityItemProperties& properties) {
@ -96,9 +96,11 @@ void ShapeEntityItem::setShape(const entity::Shape& shape) {
switch (_shape) {
case entity::Shape::Cube:
_type = EntityTypes::Box;
_collisionShapeType = ShapeType::SHAPE_TYPE_BOX;
break;
case entity::Shape::Sphere:
_type = EntityTypes::Sphere;
_collisionShapeType = ShapeType::SHAPE_TYPE_ELLIPSOID;
break;
default:
_type = EntityTypes::Shape;
@ -170,46 +172,29 @@ void ShapeEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_ALPHA, getAlpha());
}
void ShapeEntityItem::computeShapeInfo(ShapeInfo& info) {
if ( _collisionShapeType == ShapeType::SHAPE_TYPE_NONE ) {
if (_shape == entity::Shape::NUM_SHAPES)
{
EntityItem::computeShapeInfo(info);
//--EARLY EXIT--( allow default handling to process )
return;
}
_collisionShapeType = ShapeFactory::computeShapeType(getShape(), getDimensions());
}
return EntityItem::computeShapeInfo(info);
}
// This value specifes how the shape should be treated by physics calculations.
// For now, all polys will act as spheres
ShapeType ShapeEntityItem::getShapeType() const {
//TODO_CUSACK: This needs to be retrieved from properties if possible
// or stored within a new member and set during parsing of
// the properties like setShape via set/get/readEntityProperties.
// Perhaps if the _actual_ collisionShapeType is needed (the version that's in use
// based on analysis of the shape's halfExtents when BulletLibrary collision shape was
// created as opposed to the desired ShapeType is it possible to retrieve that information)?
//if (_shape == entity::Shape::Cylinder) {
// return SHAPE_TYPE_CYLINDER_Y;
//}
//TODO_CUSACK: This needs to be retrieved from properties if possible
// or stored within a new member and set during parsing of
// the properties like setShape via set/get/readEntityProperties.
// Perhaps if the _actual_ collisionShapeType is needed (the version that's in use
// based on analysis of the shape's halfExtents when BulletLibrary collision shape was
// created as opposed to the desired ShapeType is it possible to retrieve that information)?
//if (_shape == entity::Shape::Cylinder) {
// return SHAPE_TYPE_CYLINDER_Y;
//}
//// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere
//return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID;
//// Original functionality: Everything not a cube, is treated like an ellipsoid/sphere
//return (_shape == entity::Shape::Cube) ? SHAPE_TYPE_BOX : SHAPE_TYPE_ELLIPSOID;
if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE)
{
//--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere )
return SHAPE_TYPE_ELLIPSOID;
}
//if (_collisionShapeType == ShapeType::SHAPE_TYPE_NONE)
//{
// //--EARLY EXIT--( Maintain previous behavior of treating invalid as Ellipsoid/Sphere )
// return SHAPE_TYPE_ELLIPSOID;
//}
return _collisionShapeType;
return _collisionShapeType;
}
void ShapeEntityItem::setColor(const rgbColor& value) {

View file

@ -84,7 +84,6 @@ public:
QColor getQColor() const;
void setColor(const QColor& value);
void computeShapeInfo(ShapeInfo& info);
ShapeType getShapeType() const override;
bool shouldBePhysical() const override { return !isDead(); }

View file

@ -247,121 +247,120 @@ void deleteStaticMeshArray(btTriangleIndexVertexArray* dataArray) {
delete dataArray;
}
ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape *outCollisionShape = nullptr)
ShapeType validateShapeType(ShapeType type, const glm::vec3 &halfExtents, btCollisionShape **outCollisionShape = NULL)
{
if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID))
{
float radius = halfExtents.x;
const float MIN_RADIUS = 0.001f;
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
if (radius > MIN_RADIUS
&& fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR
&& fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) {
// close enough to true sphere
if (outCollisionShape) {
outCollisionShape = new btSphereShape(radius);
}
if ((type == SHAPE_TYPE_SPHERE) || (type == SHAPE_TYPE_ELLIPSOID))
{
float radius = halfExtents.x;
const float MIN_RADIUS = 0.001f;
const float MIN_RELATIVE_SPHERICAL_ERROR = 0.001f;
if (radius > MIN_RADIUS
&& fabsf(radius - halfExtents.y) / radius < MIN_RELATIVE_SPHERICAL_ERROR
&& fabsf(radius - halfExtents.z) / radius < MIN_RELATIVE_SPHERICAL_ERROR) {
// close enough to true sphere
if (outCollisionShape) {
(*outCollisionShape) = new btSphereShape(radius);
}
return SHAPE_TYPE_SPHERE;
}
else {
ShapeInfo::PointList points;
points.reserve(NUM_UNIT_SPHERE_DIRECTIONS);
for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) {
points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents);
}
if (outCollisionShape) {
outCollisionShape = createConvexHull(points);
}
return SHAPE_TYPE_SPHERE;
}
else {
ShapeInfo::PointList points;
points.reserve(NUM_UNIT_SPHERE_DIRECTIONS);
for (uint32_t i = 0; i < NUM_UNIT_SPHERE_DIRECTIONS; ++i) {
points.push_back(bulletToGLM(_unitSphereDirections[i]) * halfExtents);
}
if (outCollisionShape) {
(*outCollisionShape) = createConvexHull(points);
}
return SHAPE_TYPE_ELLIPSOID;
}
}
else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z))
{
const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);
if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) {
if (outCollisionShape) {
outCollisionShape = new btCylinderShape(btHalfExtents);
}
return SHAPE_TYPE_ELLIPSOID;
}
}
else if ((type == SHAPE_TYPE_CYLINDER_X) || (type == SHAPE_TYPE_CYLINDER_Y) || (type == SHAPE_TYPE_CYLINDER_Z))
{
// TODO_CUSACK: Should allow for minor variance along axes?
const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);
if ((halfExtents.y >= halfExtents.x) && (halfExtents.y >= halfExtents.z)) {
if (outCollisionShape) {
(*outCollisionShape) = new btCylinderShape(btHalfExtents);
}
return SHAPE_TYPE_CYLINDER_Y;
}
else if (halfExtents.x > halfExtents.z) {
if (outCollisionShape) {
outCollisionShape = new btCylinderShapeX(btHalfExtents);
}
return SHAPE_TYPE_CYLINDER_Y;
}
else if (halfExtents.x >= halfExtents.z) {
if (outCollisionShape) {
(*outCollisionShape) = new btCylinderShapeX(btHalfExtents);
}
return SHAPE_TYPE_CYLINDER_X;
}
else if (halfExtents.z > halfExtents.x) {
if (outCollisionShape) {
outCollisionShape = new btCylinderShapeZ(btHalfExtents);
}
return SHAPE_TYPE_CYLINDER_X;
}
else if (halfExtents.z > halfExtents.x) {
if (outCollisionShape) {
(*outCollisionShape) = new btCylinderShapeZ(btHalfExtents);
}
return SHAPE_TYPE_CYLINDER_Z;
}
else //...there was no major axis, treat as a sphere
{
ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape);
return cylinderFallback;
}
}
return SHAPE_TYPE_CYLINDER_Z;
}
else //...there was no major axis, treat as a sphere
{
ShapeType cylinderFallback = validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, outCollisionShape);
return cylinderFallback;
}
}
//Got here, then you are what you are along with outCollisionShape
return type;
//Got here, then you are what you are along with outCollisionShape
return type;
}
ShapeType ShapeFactory::computeShapeType(entity::Shape shape, const glm::vec3 &entityDimensions) {
if ( shape == entity::Shape::NUM_SHAPES ) {
//--EARLY EXIT--
return SHAPE_TYPE_NONE;
}
if (shape == entity::Shape::NUM_SHAPES) {
//--EARLY EXIT--
return SHAPE_TYPE_NONE;
}
const glm::vec3 halfExtents = entityDimensions * 0.5f;
switch (shape){
case entity::Shape::Triangle: {
//TODO_CUSACK: Implement this
return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents);
}
const glm::vec3 halfExtents = entityDimensions * 0.5f;
switch (shape){
case entity::Shape::Triangle: {
//TODO_CUSACK: Implement this
return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents);
}
//Note: Intentional Fallthrough from Quad to Cube
case entity::Shape::Quad:
case entity::Shape::Cube: {
return SHAPE_TYPE_BOX;
}
//Note: Intentional Fallthrough from Hexagon to Sphere
case entity::Shape::Hexagon:
case entity::Shape::Octagon:
case entity::Shape::Circle:
case entity::Shape::Sphere: {
return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents);
}
//Note: Intentional Fallthrough from Quad to Cube
case entity::Shape::Quad:
case entity::Shape::Cube: {
return SHAPE_TYPE_BOX;
}
case entity::Shape::Cylinder: {
return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents);
}
//Note: Intentional Fallthrough from Hexagon to Sphere
case entity::Shape::Hexagon:
case entity::Shape::Octagon:
case entity::Shape::Circle:
case entity::Shape::Sphere: {
return validateShapeType(SHAPE_TYPE_SPHERE, halfExtents);
}
//Note: Intentional Fallthrough from Tetrahedron to Icosahedron
case entity::Shape::Tetrahedron:
case entity::Shape::Octahedron:
case entity::Shape::Dodecahedron:
case entity::Shape::Icosahedron: {
case entity::Shape::Cylinder: {
return validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents);
}
//TODO_CUSACK: Implement the hedrons
return validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents);
}
//Note: Intentional Fallthrough from Tetrahedron to Icosahedron
case entity::Shape::Tetrahedron:
case entity::Shape::Octahedron:
case entity::Shape::Dodecahedron:
case entity::Shape::Icosahedron: {
//Note: Intentional Fallthrough from Torus to default.
case entity::Shape::Torus:
case entity::Shape::Cone: {
//TODO_CUSACK: Implement the hedrons
return validateShapeType( SHAPE_TYPE_ELLIPSOID, halfExtents );
}
//Note: Intentional Fallthrough from Torus to default.
case entity::Shape::Torus:
case entity::Shape::Cone: {
// These types are currently unsupported
}
default:
return SHAPE_TYPE_NONE;
}
// These types are currently unsupported
}
default:
return SHAPE_TYPE_NONE;
}
}
@ -379,7 +378,7 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
// shape = new btSphereShape(radius);
//}
//break;
case SHAPE_TYPE_SPHERE:
case SHAPE_TYPE_SPHERE:
case SHAPE_TYPE_ELLIPSOID: {
glm::vec3 halfExtents = info.getHalfExtents();
//float radius = halfExtents.x;
@ -399,9 +398,11 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
// shape = createConvexHull(points);
//}
validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, shape);
validateShapeType(SHAPE_TYPE_ELLIPSOID, halfExtents, &shape);
}
break;
//TODO_CUSACK: Add Capsules to vetting/validation process for
// type checks.
case SHAPE_TYPE_CAPSULE_Y: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.x;
@ -409,42 +410,44 @@ const btCollisionShape* ShapeFactory::createShapeFromInfo(const ShapeInfo& info)
shape = new btCapsuleShape(radius, height);
}
break;
case SHAPE_TYPE_CAPSULE_X: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.y;
float height = 2.0f * halfExtents.x;
shape = new btCapsuleShapeX(radius, height);
}
break;
case SHAPE_TYPE_CAPSULE_Z: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.x;
float height = 2.0f * halfExtents.z;
shape = new btCapsuleShapeZ(radius, height);
}
break;
case SHAPE_TYPE_CYLINDER_X:
case SHAPE_TYPE_CYLINDER_Z:
case SHAPE_TYPE_CYLINDER_Y: {
// TODO_CUSACK: Should allow for minor variance along axes.
const glm::vec3 halfExtents = info.getHalfExtents();
//const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);
//if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) {
// shape = new btCylinderShape(btHalfExtents);
//}
//else if (halfExtents.x > halfExtents.z) {
// shape = new btCylinderShapeX(btHalfExtents);
//}
//else if (halfExtents.z > halfExtents.x) {
// shape = new btCylinderShapeZ(btHalfExtents);
//}
//else //...there was no major axis, treat as a sphere
//{
// //TODO_CUSACK: Shunt to ELLIPSOID handling
//}
validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, shape);
}
break;
case SHAPE_TYPE_CAPSULE_X: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.y;
float height = 2.0f * halfExtents.x;
shape = new btCapsuleShapeX(radius, height);
}
break;
case SHAPE_TYPE_CAPSULE_Z: {
glm::vec3 halfExtents = info.getHalfExtents();
float radius = halfExtents.x;
float height = 2.0f * halfExtents.z;
shape = new btCapsuleShapeZ(radius, height);
}
break;
case SHAPE_TYPE_CYLINDER_X:
case SHAPE_TYPE_CYLINDER_Z:
case SHAPE_TYPE_CYLINDER_Y: {
// TODO_CUSACK: Should allow for minor variance along axes.
const glm::vec3 halfExtents = info.getHalfExtents();
//const btVector3 btHalfExtents(halfExtents.x, halfExtents.y, halfExtents.z);
//if ((halfExtents.y > halfExtents.x) && (halfExtents.y > halfExtents.z)) {
// shape = new btCylinderShape(btHalfExtents);
//}
//else if (halfExtents.x > halfExtents.z) {
// shape = new btCylinderShapeX(btHalfExtents);
//}
//else if (halfExtents.z > halfExtents.x) {
// shape = new btCylinderShapeZ(btHalfExtents);
//}
//else //...there was no major axis, treat as a sphere
//{
// //TODO_CUSACK: Shunt to ELLIPSOID handling
//}
validateShapeType(SHAPE_TYPE_CYLINDER_Y, halfExtents, &shape);
}
break;
//TODO_CUSACK: Add compound and simple hull to vetting/validation
// process for types.
case SHAPE_TYPE_COMPOUND:
case SHAPE_TYPE_SIMPLE_HULL: {
const ShapeInfo::PointCollection& pointCollection = info.getPointCollection();

View file

@ -29,7 +29,8 @@ void ShapeInfo::clear() {
}
void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString url) {
_url = "";
//TODO_CUSACK: Does this need additional cases and handling added?
_url = "";
_type = type;
setHalfExtents(halfExtents);
switch(type) {
@ -55,6 +56,8 @@ void ShapeInfo::setParams(ShapeType type, const glm::vec3& halfExtents, QString
}
void ShapeInfo::setBox(const glm::vec3& halfExtents) {
//TODO_CUSACK: Should this pointlist clearance added in case
// this is a re-purposed instance?
_url = "";
_type = SHAPE_TYPE_BOX;
setHalfExtents(halfExtents);
@ -62,6 +65,8 @@ void ShapeInfo::setBox(const glm::vec3& halfExtents) {
}
void ShapeInfo::setSphere(float radius) {
//TODO_CUSACK: Should this pointlist clearance added in case
// this is a re-purposed instance?
_url = "";
_type = SHAPE_TYPE_SPHERE;
radius = glm::max(radius, MIN_HALF_EXTENT);
@ -70,12 +75,17 @@ void ShapeInfo::setSphere(float radius) {
}
void ShapeInfo::setPointCollection(const ShapeInfo::PointCollection& pointCollection) {
//TODO_CUSACK: Should this have protection against inadvertant clearance and type
// resetting? If for some reason this was called and point list was and is emtpy
// would we still wish to clear out everything?
_pointCollection = pointCollection;
_type = (_pointCollection.size() > 0) ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
_doubleHashKey.clear();
}
void ShapeInfo::setCapsuleY(float radius, float halfHeight) {
//TODO_CUSACK: Should this pointlist clearance added in case
// this is a re-purposed instance?
_url = "";
_type = SHAPE_TYPE_CAPSULE_Y;
radius = glm::max(radius, MIN_HALF_EXTENT);
@ -117,6 +127,7 @@ int ShapeInfo::getLargestSubshapePointCount() const {
}
float ShapeInfo::computeVolume() const {
//TODO_CUSACK: Add support for other ShapeTypes.
const float DEFAULT_VOLUME = 1.0f;
float volume = DEFAULT_VOLUME;
switch(_type) {
@ -150,6 +161,7 @@ float ShapeInfo::computeVolume() const {
}
bool ShapeInfo::contains(const glm::vec3& point) const {
//TODO_CUSACK: Add support for other ShapeTypes like Ellipsoid/Compound.
switch(_type) {
case SHAPE_TYPE_SPHERE:
return glm::length(point) <= _halfExtents.x;

View file

@ -1,33 +1,30 @@
var orientation = Camera.getOrientation();
orientation = Quat.safeEulerAngles(orientation);
orientation.x = 0;
orientation = Quat.fromVec3Degrees(orientation);
var center = Vec3.sum(MyAvatar.position, Vec3.multiply(3, Quat.getForward(orientation)));
// compute a position to create the object relative to avatar
var forwardOffset = Vec3.multiply(2.0, Quat.getFront(MyAvatar.orientation));
var objectPosition = Vec3.sum(MyAvatar.position, forwardOffset);
// Math.random ensures no caching of script
var SCRIPT_URL = Script.resolvePath("myEntityScript.js")
var LIFETIME = 1800; //seconds
var DIM_HEIGHT = 1, DIM_WIDTH = 1, DIM_DEPTH = 1;
var COLOR_R = 100, COLOR_G = 10, COLOR_B = 200;
var myEntity = Entities.addEntity({
name: "ShapeSpawnTest",
type: "Shape",
shape: "Cylinder",
color: {
red: 200,
green: 10,
blue: 200
},
position: center,
dimensions: {
x: 1,
y: 1,
z: 1
},
script: SCRIPT_URL
})
var properties = {
name: "ShapeSpawnTest",
type: "Shape",
shape: "Cylinder",
dimensions: {x: DIM_WIDTH, y: DIM_HEIGHT, z: DIM_DEPTH},
color: {red: COLOR_R, green: COLOR_G, blue: COLOR_B},
position: objectPosition,
lifetime: LIFETIME,
};
// create the object
var entityId = Entities.addEntity(properties);
function cleanup() {
Entities.deleteEntity(entityId);
}
// delete the object when this script is stopped
Script.scriptEnding.connect(cleanup);
function cleanup() {
// Entities.deleteEntity(myEntity);
}
Script.scriptEnding.connect(cleanup);