mirror of
https://github.com/lubosz/overte.git
synced 2025-04-19 17:03:43 +02:00
merge from upstream
This commit is contained in:
commit
6e5aca6da4
28 changed files with 555 additions and 457 deletions
|
@ -329,7 +329,7 @@ var toolBar = (function () {
|
|||
|
||||
Script.setTimeout(resize, RESIZE_INTERVAL);
|
||||
} else {
|
||||
print("Can't add model: Model would be out of bounds.");
|
||||
Window.alert("Can't add model: Model would be out of bounds.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,7 +374,7 @@ var toolBar = (function () {
|
|||
|
||||
});
|
||||
} else {
|
||||
print("Can't create box: Box would be out of bounds.");
|
||||
Window.alert("Can't create box: Box would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -390,7 +390,7 @@ var toolBar = (function () {
|
|||
color: { red: 255, green: 0, blue: 0 }
|
||||
});
|
||||
} else {
|
||||
print("Can't create sphere: Sphere would be out of bounds.");
|
||||
Window.alert("Can't create sphere: Sphere would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ var toolBar = (function () {
|
|||
cutoff: 180, // in degrees
|
||||
});
|
||||
} else {
|
||||
print("Can't create Light: Light would be out of bounds.");
|
||||
Window.alert("Can't create Light: Light would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -433,7 +433,7 @@ var toolBar = (function () {
|
|||
lineHeight: 0.06
|
||||
});
|
||||
} else {
|
||||
print("Can't create box: Text would be out of bounds.");
|
||||
Window.alert("Can't create box: Text would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -449,7 +449,7 @@ var toolBar = (function () {
|
|||
sourceUrl: "https://highfidelity.com/",
|
||||
});
|
||||
} else {
|
||||
print("Can't create Web Entity: would be out of bounds.");
|
||||
Window.alert("Can't create Web Entity: would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -464,7 +464,7 @@ var toolBar = (function () {
|
|||
dimensions: { x: 10, y: 10, z: 10 },
|
||||
});
|
||||
} else {
|
||||
print("Can't create box: Text would be out of bounds.");
|
||||
Window.alert("Can't create box: Text would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -482,7 +482,7 @@ var toolBar = (function () {
|
|||
voxelSurfaceStyle: 1
|
||||
});
|
||||
} else {
|
||||
print("Can't create PolyVox: would be out of bounds.");
|
||||
Window.alert("Can't create PolyVox: would be out of bounds.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1068,13 +1068,16 @@ function importSVO(importURL) {
|
|||
if (Clipboard.getClipboardContentsLargestDimension() < VERY_LARGE) {
|
||||
position = getPositionToCreateEntity();
|
||||
}
|
||||
var pastedEntityIDs = Clipboard.pasteEntities(position);
|
||||
|
||||
if (isActive) {
|
||||
selectionManager.setSelections(pastedEntityIDs);
|
||||
}
|
||||
if (position.x > 0 && position.y > 0 && position.z > 0) {
|
||||
var pastedEntityIDs = Clipboard.pasteEntities(position);
|
||||
|
||||
if (isActive) {
|
||||
selectionManager.setSelections(pastedEntityIDs);
|
||||
}
|
||||
Window.raiseMainWindow();
|
||||
} else {
|
||||
Window.alert("Can't import objects: objects would be out of bounds.");
|
||||
}
|
||||
} else {
|
||||
Window.alert("There was an error importing the entity file.");
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
emitStrength: emitStrength,
|
||||
emitDirection: emitDirection,
|
||||
color: color,
|
||||
lifespan: 1.0,
|
||||
visible: true,
|
||||
locked: false });
|
||||
|
||||
|
@ -67,13 +68,13 @@
|
|||
var objs = [];
|
||||
function Init() {
|
||||
objs.push(new TestBox());
|
||||
objs.push(new TestFx({ red: 255, blue: 0, green: 0 },
|
||||
objs.push(new TestFx({ red: 255, green: 0, blue: 0 },
|
||||
{ x: 0.5, y: 1.0, z: 0.0 },
|
||||
100, 3, 1));
|
||||
objs.push(new TestFx({ red: 0, blue: 255, green: 0 },
|
||||
objs.push(new TestFx({ red: 0, green: 255, blue: 0 },
|
||||
{ x: 0, y: 1, z: 0 },
|
||||
1000, 5, 0.5));
|
||||
objs.push(new TestFx({ red: 0, blue: 0, green: 255 },
|
||||
objs.push(new TestFx({ red: 0, green: 0, blue: 255 },
|
||||
{ x: -0.5, y: 1, z: 0 },
|
||||
100, 3, 1));
|
||||
}
|
||||
|
|
|
@ -3596,24 +3596,15 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN
|
|||
int statsMessageLength = 0;
|
||||
|
||||
const QUuid& nodeUUID = sendingNode->getUUID();
|
||||
OctreeSceneStats* octreeStats;
|
||||
|
||||
|
||||
// now that we know the node ID, let's add these stats to the stats for that node...
|
||||
_octreeSceneStatsLock.lockForWrite();
|
||||
auto it = _octreeServerSceneStats.find(nodeUUID);
|
||||
if (it != _octreeServerSceneStats.end()) {
|
||||
octreeStats = &it->second;
|
||||
statsMessageLength = octreeStats->unpackFromPacket(packet);
|
||||
} else {
|
||||
OctreeSceneStats temp;
|
||||
statsMessageLength = temp.unpackFromPacket(packet);
|
||||
octreeStats = &temp;
|
||||
}
|
||||
|
||||
OctreeSceneStats& octreeStats = _octreeServerSceneStats[nodeUUID];
|
||||
statsMessageLength = octreeStats.unpackFromPacket(packet);
|
||||
|
||||
_octreeSceneStatsLock.unlock();
|
||||
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(octreeStats->getJurisdictionRoot(), rootDetails);
|
||||
|
||||
// see if this is the first we've heard of this node...
|
||||
NodeToJurisdictionMap* jurisdiction = NULL;
|
||||
QString serverType;
|
||||
|
@ -3625,6 +3616,9 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN
|
|||
jurisdiction->lockForRead();
|
||||
if (jurisdiction->find(nodeUUID) == jurisdiction->end()) {
|
||||
jurisdiction->unlock();
|
||||
|
||||
VoxelPositionSize rootDetails;
|
||||
voxelDetailsForCode(octreeStats.getJurisdictionRoot(), rootDetails);
|
||||
|
||||
qCDebug(interfaceapp, "stats from new %s server... [%f, %f, %f, %f]",
|
||||
qPrintable(serverType),
|
||||
|
@ -3637,7 +3631,7 @@ int Application::processOctreeStats(NLPacket& packet, SharedNodePointer sendingN
|
|||
// but OctreeSceneStats thinks it's just returning a reference to its contents. So we need to make a copy of the
|
||||
// details from the OctreeSceneStats to construct the JurisdictionMap
|
||||
JurisdictionMap jurisdictionMap;
|
||||
jurisdictionMap.copyContents(octreeStats->getJurisdictionRoot(), octreeStats->getJurisdictionEndNodes());
|
||||
jurisdictionMap.copyContents(octreeStats.getJurisdictionRoot(), octreeStats.getJurisdictionEndNodes());
|
||||
jurisdiction->lockForWrite();
|
||||
(*jurisdiction)[nodeUUID] = jurisdictionMap;
|
||||
jurisdiction->unlock();
|
||||
|
|
|
@ -431,15 +431,17 @@ void Avatar::render(RenderArgs* renderArgs, const glm::vec3& cameraPosition) {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO: re-implement these when we have more detailed avatar collision shapes
|
||||
bool renderSkeleton = Menu::getInstance()->isOptionChecked(MenuOption::RenderSkeletonCollisionShapes);
|
||||
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
|
||||
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
|
||||
if (renderSkeleton) {
|
||||
_skeletonModel.renderJointCollisionShapes(0.7f);
|
||||
}
|
||||
bool renderHead = Menu::getInstance()->isOptionChecked(MenuOption::RenderHeadCollisionShapes);
|
||||
if (renderHead && shouldRenderHead(renderArgs)) {
|
||||
getHead()->getFaceModel().renderJointCollisionShapes(0.7f);
|
||||
}
|
||||
*/
|
||||
|
||||
bool renderBounding = Menu::getInstance()->isOptionChecked(MenuOption::RenderBoundingCollisionShapes);
|
||||
if (renderBounding && shouldRenderHead(renderArgs)) {
|
||||
_skeletonModel.renderBoundingCollisionShapes(*renderArgs->_batch, 0.7f);
|
||||
}
|
||||
|
@ -794,33 +796,6 @@ void Avatar::renderDisplayName(gpu::Batch& batch, const ViewFrustum& frustum, co
|
|||
renderer->draw(batch, text_x, -text_y, nameUTF8.data(), textColor);
|
||||
}
|
||||
|
||||
bool Avatar::findRayIntersection(RayIntersectionInfo& intersection) const {
|
||||
bool hit = _skeletonModel.findRayIntersection(intersection);
|
||||
hit = getHead()->getFaceModel().findRayIntersection(intersection) || hit;
|
||||
return hit;
|
||||
}
|
||||
|
||||
bool Avatar::findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions) {
|
||||
return _skeletonModel.findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||
// TODO: Andrew to fix: Temporarily disabling collisions against the head
|
||||
//return getHead()->getFaceModel().findSphereCollisions(penetratorCenter, penetratorRadius, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
|
||||
return _skeletonModel.findPlaneCollisions(plane, collisions) ||
|
||||
getHead()->getFaceModel().findPlaneCollisions(plane, collisions);
|
||||
}
|
||||
|
||||
bool Avatar::findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions) {
|
||||
// TODO: Andrew to fix: also collide against _skeleton
|
||||
//bool collided = _skeletonModel.findCollisions(shapes, collisions);
|
||||
|
||||
Model& headModel = getHead()->getFaceModel();
|
||||
//collided = headModel.findCollisions(shapes, collisions) || collided;
|
||||
bool collided = headModel.findCollisions(shapes, collisions);
|
||||
return collided;
|
||||
}
|
||||
|
||||
void Avatar::setSkeletonOffset(const glm::vec3& offset) {
|
||||
const float MAX_OFFSET_LENGTH = _scale * 0.5f;
|
||||
float offsetLength = glm::length(offset);
|
||||
|
@ -1140,9 +1115,8 @@ void Avatar::setShowDisplayName(bool showDisplayName) {
|
|||
|
||||
// virtual
|
||||
void Avatar::computeShapeInfo(ShapeInfo& shapeInfo) {
|
||||
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
||||
shapeInfo.setCapsuleY(capsule.getRadius(), capsule.getHalfHeight());
|
||||
shapeInfo.setOffset(_skeletonModel.getBoundingShapeOffset());
|
||||
shapeInfo.setCapsuleY(_skeletonModel.getBoundingCapsuleRadius(), 0.5f * _skeletonModel.getBoundingCapsuleHeight());
|
||||
shapeInfo.setOffset(_skeletonModel.getBoundingCapsuleOffset());
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
|
@ -110,26 +110,6 @@ public:
|
|||
/// Returns the distance to use as a LOD parameter.
|
||||
float getLODDistance() const;
|
||||
|
||||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
||||
|
||||
/// \param shapes list of shapes to collide against avatar
|
||||
/// \param collisions list to store collision results
|
||||
/// \return true if at least one shape collided with avatar
|
||||
bool findCollisions(const QVector<const Shape*>& shapes, CollisionList& collisions);
|
||||
|
||||
/// Checks for penetration between the a sphere and the avatar's models.
|
||||
/// \param penetratorCenter the center of the penetration test sphere
|
||||
/// \param penetratorRadius the radius of the penetration test sphere
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \return whether or not the sphere penetrated
|
||||
bool findSphereCollisions(const glm::vec3& penetratorCenter, float penetratorRadius, CollisionList& collisions);
|
||||
|
||||
/// Checks for penetration between the described plane and the avatar.
|
||||
/// \param plane the penetration plane
|
||||
/// \param collisions[out] a list to which collisions get appended
|
||||
/// \return whether or not the plane penetrated
|
||||
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
|
||||
|
||||
virtual bool isMyAvatar() const { return false; }
|
||||
|
||||
virtual QVector<glm::quat> getJointRotations() const;
|
||||
|
|
|
@ -110,8 +110,6 @@ MyAvatar::MyAvatar(RigPointer rig) :
|
|||
_driveKeys[i] = 0.0f;
|
||||
}
|
||||
|
||||
_skeletonModel.setEnableShapes(true);
|
||||
|
||||
// connect to AddressManager signal for location jumps
|
||||
connect(DependencyManager::get<AddressManager>().data(), &AddressManager::locationChangeRequired,
|
||||
this, &MyAvatar::goToLocation);
|
||||
|
@ -742,6 +740,7 @@ void MyAvatar::loadData() {
|
|||
setCollisionSoundURL(settings.value("collisionSoundURL", DEFAULT_AVATAR_COLLISION_SOUND_URL).toString());
|
||||
|
||||
settings.endGroup();
|
||||
_rig->setEnableRig(settings.value("enableRig").toBool());
|
||||
}
|
||||
|
||||
void MyAvatar::saveAttachmentData(const AttachmentData& attachment) const {
|
||||
|
@ -1090,11 +1089,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const {
|
|||
|
||||
void MyAvatar::rebuildSkeletonBody() {
|
||||
// compute localAABox
|
||||
const CapsuleShape& capsule = _skeletonModel.getBoundingShape();
|
||||
float radius = capsule.getRadius();
|
||||
float height = 2.0f * (capsule.getHalfHeight() + radius);
|
||||
float radius = _skeletonModel.getBoundingCapsuleRadius();
|
||||
float height = _skeletonModel.getBoundingCapsuleHeight() + 2.0f * radius;
|
||||
glm::vec3 corner(-radius, -0.5f * height, -radius);
|
||||
corner += _skeletonModel.getBoundingShapeOffset();
|
||||
corner += _skeletonModel.getBoundingCapsuleOffset();
|
||||
glm::vec3 scale(2.0f * radius, height, 2.0f * radius);
|
||||
_characterController.setLocalBoundingBox(corner, scale);
|
||||
}
|
||||
|
|
|
@ -12,9 +12,7 @@
|
|||
#include <glm/gtx/transform.hpp>
|
||||
#include <QMultiMap>
|
||||
|
||||
#include <CapsuleShape.h>
|
||||
#include <DeferredLightingEffect.h>
|
||||
#include <SphereShape.h>
|
||||
|
||||
#include "Application.h"
|
||||
#include "Avatar.h"
|
||||
|
@ -34,8 +32,9 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r
|
|||
Model(rig, parent),
|
||||
_triangleFanID(DependencyManager::get<GeometryCache>()->allocateID()),
|
||||
_owningAvatar(owningAvatar),
|
||||
_boundingShape(),
|
||||
_boundingShapeLocalOffset(0.0f),
|
||||
_boundingCapsuleLocalOffset(0.0f),
|
||||
_boundingCapsuleRadius(0.0f),
|
||||
_boundingCapsuleHeight(0.0f),
|
||||
_defaultEyeModelPosition(glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
_standingFoot(NO_FOOT),
|
||||
_standingOffset(0.0f),
|
||||
|
@ -44,7 +43,6 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar, QObject* parent, RigPointer r
|
|||
{
|
||||
assert(_rig);
|
||||
assert(_owningAvatar);
|
||||
_enableShapes = true;
|
||||
}
|
||||
|
||||
SkeletonModel::~SkeletonModel() {
|
||||
|
@ -81,10 +79,7 @@ void SkeletonModel::initJointStates(QVector<JointState> states) {
|
|||
_rig->updateJointState(i, parentTransform);
|
||||
}
|
||||
|
||||
clearShapes();
|
||||
if (_enableShapes) {
|
||||
buildShapes();
|
||||
}
|
||||
buildShapes();
|
||||
|
||||
Extents meshExtents = getMeshExtents();
|
||||
_headClipDistance = -(meshExtents.minimum.z / _scale.z - _defaultEyeModelPosition.z);
|
||||
|
@ -120,6 +115,10 @@ void SkeletonModel::updateClusterMatrices() {
|
|||
}
|
||||
}
|
||||
|
||||
void SkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) {
|
||||
_rig->simulateInternal(deltaTime, parentTransform, _owningAvatar->getPosition(), _owningAvatar->getVelocity(), _owningAvatar->getOrientation());
|
||||
}
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
setTranslation(_owningAvatar->getSkeletonPosition());
|
||||
static const glm::quat refOrientation = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
@ -183,9 +182,6 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
|||
if (_rig->getJointsAreDirty()) {
|
||||
updateClusterMatrices();
|
||||
}
|
||||
|
||||
_boundingShape.setTranslation(_translation + _rotation * _boundingShapeLocalOffset);
|
||||
_boundingShape.setRotation(_rotation);
|
||||
}
|
||||
|
||||
void SkeletonModel::renderIKConstraints(gpu::Batch& batch) {
|
||||
|
@ -677,12 +673,11 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
|||
// NOTE: we assume that the longest side of totalExtents is the yAxis...
|
||||
glm::vec3 diagonal = totalExtents.maximum - totalExtents.minimum;
|
||||
// ... and assume the radius is half the RMS of the X and Z sides:
|
||||
float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
||||
_boundingShape.setRadius(capsuleRadius);
|
||||
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
|
||||
_boundingCapsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
|
||||
_boundingCapsuleHeight = diagonal.y - 2.0f * _boundingCapsuleRadius;
|
||||
|
||||
glm::vec3 rootPosition = _rig->getJointState(geometry.rootJointIndex).getPosition();
|
||||
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
||||
_boundingCapsuleLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
|
||||
_boundingRadius = 0.5f * glm::length(diagonal);
|
||||
}
|
||||
|
||||
|
@ -693,30 +688,26 @@ void SkeletonModel::renderBoundingCollisionShapes(gpu::Batch& batch, float alpha
|
|||
auto deferredLighting = DependencyManager::get<DeferredLightingEffect>();
|
||||
Transform transform; // = Transform();
|
||||
|
||||
// draw a blue sphere at the capsule end point
|
||||
glm::vec3 endPoint;
|
||||
_boundingShape.getEndPoint(endPoint);
|
||||
endPoint = endPoint + _translation;
|
||||
transform.setTranslation(endPoint);
|
||||
// draw a blue sphere at the capsule top point
|
||||
glm::vec3 topPoint = _translation + _boundingCapsuleLocalOffset + (0.5f * _boundingCapsuleHeight) * glm::vec3(0.0f, 1.0f, 0.0f);
|
||||
transform.setTranslation(topPoint);
|
||||
batch.setModelTransform(transform);
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
glm::vec4(0.6f, 0.6f, 0.8f, alpha));
|
||||
|
||||
// draw a yellow sphere at the capsule start point
|
||||
glm::vec3 startPoint;
|
||||
_boundingShape.getStartPoint(startPoint);
|
||||
startPoint = startPoint + _translation;
|
||||
glm::vec3 axis = endPoint - startPoint;
|
||||
transform.setTranslation(startPoint);
|
||||
// draw a yellow sphere at the capsule bottom point
|
||||
glm::vec3 bottomPoint = topPoint - glm::vec3(0.0f, -_boundingCapsuleHeight, 0.0f);
|
||||
glm::vec3 axis = topPoint - bottomPoint;
|
||||
transform.setTranslation(bottomPoint);
|
||||
batch.setModelTransform(transform);
|
||||
deferredLighting->bindSimpleProgram(batch);
|
||||
geometryCache->renderSphere(batch, _boundingShape.getRadius(), BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
geometryCache->renderSphere(batch, _boundingCapsuleRadius, BALL_SUBDIVISIONS, BALL_SUBDIVISIONS,
|
||||
glm::vec4(0.8f, 0.8f, 0.6f, alpha));
|
||||
|
||||
// draw a green cylinder between the two points
|
||||
glm::vec3 origin(0.0f);
|
||||
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingShape.getRadius(), _boundingShape.getRadius(),
|
||||
Avatar::renderJointConnectingCone(batch, origin, axis, _boundingCapsuleRadius, _boundingCapsuleRadius,
|
||||
glm::vec4(0.6f, 0.8f, 0.6f, alpha));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#define hifi_SkeletonModel_h
|
||||
|
||||
|
||||
#include <CapsuleShape.h>
|
||||
#include <Model.h>
|
||||
|
||||
class Avatar;
|
||||
|
@ -30,7 +29,8 @@ public:
|
|||
|
||||
virtual void initJointStates(QVector<JointState> states);
|
||||
|
||||
void simulate(float deltaTime, bool fullUpdate = true);
|
||||
virtual void simulate(float deltaTime, bool fullUpdate = true);
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform);
|
||||
|
||||
void renderIKConstraints(gpu::Batch& batch);
|
||||
|
||||
|
@ -98,9 +98,9 @@ public:
|
|||
|
||||
void computeBoundingShape(const FBXGeometry& geometry);
|
||||
void renderBoundingCollisionShapes(gpu::Batch& batch, float alpha);
|
||||
float getBoundingShapeRadius() const { return _boundingShape.getRadius(); }
|
||||
const CapsuleShape& getBoundingShape() const { return _boundingShape; }
|
||||
const glm::vec3 getBoundingShapeOffset() const { return _boundingShapeLocalOffset; }
|
||||
float getBoundingCapsuleRadius() const { return _boundingCapsuleRadius; }
|
||||
float getBoundingCapsuleHeight() const { return _boundingCapsuleHeight; }
|
||||
const glm::vec3 getBoundingCapsuleOffset() const { return _boundingCapsuleLocalOffset; }
|
||||
|
||||
bool hasSkeleton();
|
||||
|
||||
|
@ -157,8 +157,9 @@ private:
|
|||
|
||||
Avatar* _owningAvatar;
|
||||
|
||||
CapsuleShape _boundingShape;
|
||||
glm::vec3 _boundingShapeLocalOffset;
|
||||
glm::vec3 _boundingCapsuleLocalOffset;
|
||||
float _boundingCapsuleRadius;
|
||||
float _boundingCapsuleHeight;
|
||||
|
||||
glm::vec3 _defaultEyeModelPosition;
|
||||
int _standingFoot;
|
||||
|
|
|
@ -341,17 +341,16 @@ glm::mat4 Rig::getJointVisibleTransform(int jointIndex) const {
|
|||
return maybeCauterizeHead(jointIndex).getVisibleTransform();
|
||||
}
|
||||
|
||||
void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::quat& worldRotation) {
|
||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||
glm::vec3 delta = worldPosition - _lastPosition ;
|
||||
float forwardSpeed = glm::dot(delta, front) / deltaTime;
|
||||
float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime;
|
||||
bool isWalking = std::abs(forwardSpeed) > 0.01f;
|
||||
bool isTurning = std::abs(rotationalSpeed) > 0.5f;
|
||||
void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation) {
|
||||
|
||||
// Crude, until we have blending:
|
||||
const float EXPECTED_INTERVAL = 1.0f / 60.0f;
|
||||
if (deltaTime >= EXPECTED_INTERVAL) {
|
||||
if (_enableRig) {
|
||||
glm::vec3 front = worldRotation * IDENTITY_FRONT;
|
||||
float forwardSpeed = glm::dot(worldVelocity, front);
|
||||
float rotationalSpeed = glm::angle(front, _lastFront) / deltaTime;
|
||||
bool isWalking = std::abs(forwardSpeed) > 0.01f;
|
||||
bool isTurning = std::abs(rotationalSpeed) > 0.5f;
|
||||
|
||||
// Crude, until we have blending:
|
||||
isTurning = isTurning && !isWalking; // Only one of walk/turn, walk wins.
|
||||
isTurning = false; // FIXME
|
||||
bool isIdle = !isWalking && !isTurning;
|
||||
|
@ -361,19 +360,15 @@ void Rig::simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm
|
|||
QString toStop = singleRole(_isWalking && !isWalking, _isTurning && !isTurning, _isIdle && !isIdle);
|
||||
if (!toStop.isEmpty()) {
|
||||
//qCDebug(animation) << "isTurning" << isTurning << "fronts" << front << _lastFront << glm::angle(front, _lastFront) << rotationalSpeed;
|
||||
//stopAnimationByRole(toStop);
|
||||
stopAnimationByRole(toStop);
|
||||
}
|
||||
QString newRole = singleRole(isWalking && !_isWalking, isTurning && !_isTurning, isIdle && !_isIdle);
|
||||
if (!newRole.isEmpty()) {
|
||||
//startAnimationByRole(newRole);
|
||||
qCDebug(animation) << deltaTime << ":" /*<< _lastPosition << worldPosition << "=>" */<< delta << "." << front << "=> " << forwardSpeed << newRole;
|
||||
/*if (newRole == "idle") {
|
||||
qCDebug(animation) << deltaTime << ":" << _lastPosition << worldPosition << "=>" << delta;
|
||||
}*/
|
||||
startAnimationByRole(newRole);
|
||||
qCDebug(animation) << deltaTime << ":" << worldVelocity << "." << front << "=> " << forwardSpeed << newRole;
|
||||
}
|
||||
|
||||
|
||||
_lastPosition = worldPosition;
|
||||
_positions[(++_positionIndex) % _positions.count()] = worldPosition; // exp. alt. to above line
|
||||
_lastFront = front;
|
||||
_isWalking = isWalking;
|
||||
_isTurning = isTurning;
|
||||
|
|
|
@ -108,7 +108,7 @@ public:
|
|||
void setJointTransform(int jointIndex, glm::mat4 newTransform);
|
||||
glm::mat4 getJointVisibleTransform(int jointIndex) const;
|
||||
void setJointVisibleTransform(int jointIndex, glm::mat4 newTransform);
|
||||
void simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::quat& worldRotation);
|
||||
void simulateInternal(float deltaTime, glm::mat4 parentTransform, const glm::vec3& worldPosition, const glm::vec3& worldVelocity, const glm::quat& worldRotation);
|
||||
bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation,
|
||||
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority,
|
||||
const QVector<int>& freeLineage, glm::mat4 parentTransform);
|
||||
|
@ -132,6 +132,7 @@ public:
|
|||
virtual bool getIsFirstPerson() const { return _isFirstPerson; }
|
||||
|
||||
bool getJointsAreDirty() { return _jointsAreDirty; }
|
||||
void setEnableRig(bool isEnabled) { _enableRig = isEnabled; }
|
||||
|
||||
protected:
|
||||
QVector<JointState> _jointStates;
|
||||
|
@ -146,15 +147,12 @@ public:
|
|||
bool _jointsAreDirty = false;
|
||||
int _neckJointIndex = -1;
|
||||
|
||||
bool _enableRig;
|
||||
bool _isWalking;
|
||||
bool _isTurning;
|
||||
bool _isIdle;
|
||||
glm::vec3 _lastFront;
|
||||
glm::vec3 _lastPosition;
|
||||
// or, experimentally...
|
||||
QVector<glm::vec3> _positions = QVector<glm::vec3>(4);
|
||||
QVector<float> _timeIntervals = QVector<float>(4);
|
||||
int _positionIndex;
|
||||
};
|
||||
|
||||
#endif /* defined(__hifi__Rig__) */
|
||||
|
|
|
@ -1087,7 +1087,6 @@ void AvatarData::setJointMappingsFromNetworkReply() {
|
|||
}
|
||||
|
||||
networkReply->deleteLater();
|
||||
emit jointMappingLoaded();
|
||||
}
|
||||
|
||||
void AvatarData::sendAvatarDataPacket() {
|
||||
|
|
|
@ -312,9 +312,6 @@ public:
|
|||
|
||||
bool shouldDie() const { return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; }
|
||||
|
||||
signals:
|
||||
void jointMappingLoaded(); // So that test cases or anyone waiting on asynchronous loading can be informed.
|
||||
|
||||
public slots:
|
||||
void sendAvatarDataPacket();
|
||||
void sendIdentityPacket();
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
set(TARGET_NAME entities-renderer)
|
||||
|
||||
AUTOSCRIBE_SHADER_LIB(gpu model render)
|
||||
|
||||
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
|
||||
setup_hifi_library(Widgets OpenGL Network Script)
|
||||
|
||||
|
|
|
@ -14,22 +14,141 @@
|
|||
#include <DeferredLightingEffect.h>
|
||||
#include <PerfStat.h>
|
||||
#include <GeometryCache.h>
|
||||
#include <AbstractViewStateInterface.h>
|
||||
#include "EntitiesRendererLogging.h"
|
||||
|
||||
#include "RenderableParticleEffectEntityItem.h"
|
||||
|
||||
#include "untextured_particle_vert.h"
|
||||
#include "untextured_particle_frag.h"
|
||||
#include "textured_particle_vert.h"
|
||||
#include "textured_particle_frag.h"
|
||||
|
||||
class ParticlePayload {
|
||||
public:
|
||||
typedef render::Payload<ParticlePayload> Payload;
|
||||
typedef Payload::DataPointer Pointer;
|
||||
typedef RenderableParticleEffectEntityItem::Vertex Vertex;
|
||||
|
||||
ParticlePayload() : _vertexFormat(std::make_shared<gpu::Stream::Format>()),
|
||||
_vertexBuffer(std::make_shared<gpu::Buffer>()),
|
||||
_indexBuffer(std::make_shared<gpu::Buffer>()) {
|
||||
_vertexFormat->setAttribute(gpu::Stream::POSITION, 0, gpu::Element::VEC3F_XYZ, 0);
|
||||
_vertexFormat->setAttribute(gpu::Stream::TEXCOORD, 0, gpu::Element(gpu::VEC2, gpu::FLOAT, gpu::UV), offsetof(Vertex, uv));
|
||||
_vertexFormat->setAttribute(gpu::Stream::COLOR, 0, gpu::Element::COLOR_RGBA_32, offsetof(Vertex, rgba));
|
||||
}
|
||||
|
||||
void setPipeline(gpu::PipelinePointer pipeline) { _pipeline = pipeline; }
|
||||
const gpu::PipelinePointer& getPipeline() const { return _pipeline; }
|
||||
|
||||
const Transform& getModelTransform() const { return _modelTransform; }
|
||||
void setModelTransform(const Transform& modelTransform) { _modelTransform = modelTransform; }
|
||||
|
||||
const AABox& getBound() const { return _bound; }
|
||||
void setBound(AABox& bound) { _bound = bound; }
|
||||
|
||||
gpu::BufferPointer getVertexBuffer() { return _vertexBuffer; }
|
||||
const gpu::BufferPointer& getVertexBuffer() const { return _vertexBuffer; }
|
||||
|
||||
gpu::BufferPointer getIndexBuffer() { return _indexBuffer; }
|
||||
const gpu::BufferPointer& getIndexBuffer() const { return _indexBuffer; }
|
||||
|
||||
void setTexture(gpu::TexturePointer texture) { _texture = texture; }
|
||||
const gpu::TexturePointer& getTexture() const { return _texture; }
|
||||
|
||||
bool getVisibleFlag() const { return _visibleFlag; }
|
||||
void setVisibleFlag(bool visibleFlag) { _visibleFlag = visibleFlag; }
|
||||
|
||||
void render(RenderArgs* args) const {
|
||||
assert(_pipeline);
|
||||
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
batch.setPipeline(_pipeline);
|
||||
|
||||
if (_texture) {
|
||||
batch.setResourceTexture(0, _texture);
|
||||
}
|
||||
|
||||
batch.setModelTransform(_modelTransform);
|
||||
batch.setInputFormat(_vertexFormat);
|
||||
batch.setInputBuffer(0, _vertexBuffer, 0, sizeof(Vertex));
|
||||
batch.setIndexBuffer(gpu::UINT16, _indexBuffer, 0);
|
||||
|
||||
auto numIndices = _indexBuffer->getSize() / sizeof(uint16_t);
|
||||
batch.drawIndexed(gpu::TRIANGLES, numIndices);
|
||||
}
|
||||
|
||||
protected:
|
||||
Transform _modelTransform;
|
||||
AABox _bound;
|
||||
gpu::PipelinePointer _pipeline;
|
||||
gpu::Stream::FormatPointer _vertexFormat;
|
||||
gpu::BufferPointer _vertexBuffer;
|
||||
gpu::BufferPointer _indexBuffer;
|
||||
gpu::TexturePointer _texture;
|
||||
bool _visibleFlag = true;
|
||||
};
|
||||
|
||||
namespace render {
|
||||
template <>
|
||||
const ItemKey payloadGetKey(const ParticlePayload::Pointer& payload) {
|
||||
if (payload->getVisibleFlag()) {
|
||||
return ItemKey::Builder::transparentShape();
|
||||
} else {
|
||||
return ItemKey::Builder().withInvisible().build();
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
const Item::Bound payloadGetBound(const ParticlePayload::Pointer& payload) {
|
||||
return payload->getBound();
|
||||
}
|
||||
|
||||
template <>
|
||||
void payloadRender(const ParticlePayload::Pointer& payload, RenderArgs* args) {
|
||||
payload->render(args);
|
||||
}
|
||||
}
|
||||
|
||||
gpu::PipelinePointer RenderableParticleEffectEntityItem::_texturedPipeline;
|
||||
gpu::PipelinePointer RenderableParticleEffectEntityItem::_untexturedPipeline;
|
||||
|
||||
EntityItemPointer RenderableParticleEffectEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
|
||||
return std::make_shared<RenderableParticleEffectEntityItem>(entityID, properties);
|
||||
}
|
||||
|
||||
RenderableParticleEffectEntityItem::RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
|
||||
ParticleEffectEntityItem(entityItemID, properties) {
|
||||
_cacheID = DependencyManager::get<GeometryCache>()->allocateID();
|
||||
|
||||
// lazy creation of particle system pipeline
|
||||
if (!_untexturedPipeline && !_texturedPipeline) {
|
||||
createPipelines();
|
||||
}
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
|
||||
Q_ASSERT(getType() == EntityTypes::ParticleEffect);
|
||||
PerformanceTimer perfTimer("RenderableParticleEffectEntityItem::render");
|
||||
bool RenderableParticleEffectEntityItem::addToScene(EntityItemPointer self,
|
||||
render::ScenePointer scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
|
||||
auto particlePayload = std::shared_ptr<ParticlePayload>(new ParticlePayload());
|
||||
particlePayload->setPipeline(_untexturedPipeline);
|
||||
_renderItemId = scene->allocateID();
|
||||
auto renderData = ParticlePayload::Pointer(particlePayload);
|
||||
auto renderPayload = render::PayloadPointer(new ParticlePayload::Payload(renderData));
|
||||
pendingChanges.resetItem(_renderItemId, renderPayload);
|
||||
_scene = scene;
|
||||
return true;
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::removeFromScene(EntityItemPointer self,
|
||||
render::ScenePointer scene,
|
||||
render::PendingChanges& pendingChanges) {
|
||||
pendingChanges.removeItem(_renderItemId);
|
||||
_scene = nullptr;
|
||||
};
|
||||
|
||||
void RenderableParticleEffectEntityItem::update(const quint64& now) {
|
||||
ParticleEffectEntityItem::update(now);
|
||||
|
||||
if (_texturesChangedFlag) {
|
||||
if (_textures.isEmpty()) {
|
||||
|
@ -42,71 +161,155 @@ void RenderableParticleEffectEntityItem::render(RenderArgs* args) {
|
|||
_texturesChangedFlag = false;
|
||||
}
|
||||
|
||||
bool textured = _texture && _texture->isLoaded();
|
||||
updateQuads(args, textured);
|
||||
|
||||
Q_ASSERT(args->_batch);
|
||||
gpu::Batch& batch = *args->_batch;
|
||||
if (textured) {
|
||||
batch.setResourceTexture(0, _texture->getGPUTexture());
|
||||
}
|
||||
batch.setModelTransform(getTransformToCenter());
|
||||
DependencyManager::get<DeferredLightingEffect>()->bindSimpleProgram(batch, textured);
|
||||
DependencyManager::get<GeometryCache>()->renderVertices(batch, gpu::QUADS, _cacheID);
|
||||
};
|
||||
updateRenderItem();
|
||||
}
|
||||
|
||||
static glm::vec3 zSortAxis;
|
||||
static bool zSort(const glm::vec3& rhs, const glm::vec3& lhs) {
|
||||
return glm::dot(rhs, ::zSortAxis) > glm::dot(lhs, ::zSortAxis);
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::updateQuads(RenderArgs* args, bool textured) {
|
||||
float particleRadius = getParticleRadius();
|
||||
glm::vec4 particleColor(toGlm(getXColor()), getLocalRenderAlpha());
|
||||
|
||||
glm::vec3 upOffset = args->_viewFrustum->getUp() * particleRadius;
|
||||
glm::vec3 rightOffset = args->_viewFrustum->getRight() * particleRadius;
|
||||
|
||||
QVector<glm::vec3> vertices;
|
||||
QVector<glm::vec3> positions;
|
||||
QVector<glm::vec2> textureCoords;
|
||||
vertices.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE);
|
||||
|
||||
if (textured) {
|
||||
textureCoords.reserve(getLivingParticleCount() * VERTS_PER_PARTICLE);
|
||||
}
|
||||
positions.reserve(getLivingParticleCount());
|
||||
|
||||
|
||||
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
|
||||
positions.append(_particlePositions[i]);
|
||||
if (textured) {
|
||||
textureCoords.append(glm::vec2(0, 1));
|
||||
textureCoords.append(glm::vec2(1, 1));
|
||||
textureCoords.append(glm::vec2(1, 0));
|
||||
textureCoords.append(glm::vec2(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// sort particles back to front
|
||||
::zSortAxis = args->_viewFrustum->getDirection();
|
||||
qSort(positions.begin(), positions.end(), zSort);
|
||||
|
||||
for (int i = 0; i < positions.size(); i++) {
|
||||
glm::vec3 pos = (textured) ? positions[i] : _particlePositions[i];
|
||||
|
||||
// generate corners of quad aligned to face the camera.
|
||||
vertices.append(pos + rightOffset + upOffset);
|
||||
vertices.append(pos - rightOffset + upOffset);
|
||||
vertices.append(pos - rightOffset - upOffset);
|
||||
vertices.append(pos + rightOffset - upOffset);
|
||||
|
||||
}
|
||||
|
||||
if (textured) {
|
||||
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, textureCoords, particleColor);
|
||||
} else {
|
||||
DependencyManager::get<GeometryCache>()->updateVertices(_cacheID, vertices, particleColor);
|
||||
}
|
||||
uint32_t toRGBA(uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
|
||||
return ((uint32_t)r | (uint32_t)g << 8 | (uint32_t)b << 16 | (uint32_t)a << 24);
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::updateRenderItem() {
|
||||
|
||||
if (!_scene) {
|
||||
return;
|
||||
}
|
||||
|
||||
float particleRadius = getParticleRadius();
|
||||
auto xcolor = getXColor();
|
||||
auto alpha = (uint8_t)(glm::clamp(getLocalRenderAlpha(), 0.0f, 1.0f) * 255.0f);
|
||||
auto rgba = toRGBA(xcolor.red, xcolor.green, xcolor.blue, alpha);
|
||||
|
||||
// make a copy of each particle position
|
||||
std::vector<glm::vec3> positions;
|
||||
positions.reserve(getLivingParticleCount());
|
||||
for (quint32 i = _particleHeadIndex; i != _particleTailIndex; i = (i + 1) % _maxParticles) {
|
||||
positions.push_back(_particlePositions[i]);
|
||||
}
|
||||
|
||||
// sort particles back to front
|
||||
// NOTE: this is view frustum might be one frame out of date.
|
||||
auto frustum = AbstractViewStateInterface::instance()->getCurrentViewFrustum();
|
||||
::zSortAxis = frustum->getDirection();
|
||||
qSort(positions.begin(), positions.end(), zSort);
|
||||
|
||||
// allocate vertices
|
||||
_vertices.clear();
|
||||
|
||||
// build vertices from particle positions
|
||||
const glm::vec3 upOffset = frustum->getUp() * particleRadius;
|
||||
const glm::vec3 rightOffset = frustum->getRight() * particleRadius;
|
||||
for (auto&& pos : positions) {
|
||||
// generate corners of quad aligned to face the camera.
|
||||
_vertices.emplace_back(pos + rightOffset + upOffset, glm::vec2(1.0f, 1.0f), rgba);
|
||||
_vertices.emplace_back(pos - rightOffset + upOffset, glm::vec2(0.0f, 1.0f), rgba);
|
||||
_vertices.emplace_back(pos - rightOffset - upOffset, glm::vec2(0.0f, 0.0f), rgba);
|
||||
_vertices.emplace_back(pos + rightOffset - upOffset, glm::vec2(1.0f, 0.0f), rgba);
|
||||
}
|
||||
|
||||
render::PendingChanges pendingChanges;
|
||||
pendingChanges.updateItem<ParticlePayload>(_renderItemId, [&](ParticlePayload& payload) {
|
||||
|
||||
// update vertex buffer
|
||||
auto vertexBuffer = payload.getVertexBuffer();
|
||||
size_t numBytes = sizeof(Vertex) * _vertices.size();
|
||||
vertexBuffer->resize(numBytes);
|
||||
gpu::Byte* data = vertexBuffer->editData();
|
||||
memcpy(data, &(_vertices[0]), numBytes);
|
||||
|
||||
// FIXME, don't update index buffer if num particles has not changed.
|
||||
// update index buffer
|
||||
auto indexBuffer = payload.getIndexBuffer();
|
||||
const size_t NUM_VERTS_PER_PARTICLE = 4;
|
||||
const size_t NUM_INDICES_PER_PARTICLE = 6;
|
||||
auto numQuads = (_vertices.size() / NUM_VERTS_PER_PARTICLE);
|
||||
numBytes = sizeof(uint16_t) * numQuads * NUM_INDICES_PER_PARTICLE;
|
||||
indexBuffer->resize(numBytes);
|
||||
data = indexBuffer->editData();
|
||||
auto indexPtr = reinterpret_cast<uint16_t*>(data);
|
||||
for (size_t i = 0; i < numQuads; ++i) {
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 0] = i * NUM_VERTS_PER_PARTICLE + 0;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 1] = i * NUM_VERTS_PER_PARTICLE + 1;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 2] = i * NUM_VERTS_PER_PARTICLE + 3;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 3] = i * NUM_VERTS_PER_PARTICLE + 1;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 4] = i * NUM_VERTS_PER_PARTICLE + 2;
|
||||
indexPtr[i * NUM_INDICES_PER_PARTICLE + 5] = i * NUM_VERTS_PER_PARTICLE + 3;
|
||||
}
|
||||
|
||||
// update transform
|
||||
glm::quat rot = _transform.getRotation();
|
||||
glm::vec3 pos = _transform.getTranslation();
|
||||
Transform t;
|
||||
t.setRotation(rot);
|
||||
t.setTranslation(pos);
|
||||
payload.setModelTransform(t);
|
||||
|
||||
// transform _particleMinBound and _particleMaxBound corners into world coords
|
||||
glm::vec3 d = _particleMaxBound - _particleMinBound;
|
||||
const size_t NUM_BOX_CORNERS = 8;
|
||||
glm::vec3 corners[NUM_BOX_CORNERS] = {
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, 0.0f)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, 0.0f, d.z)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, 0.0f, d.z)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(0.0f, d.y, d.z)),
|
||||
pos + rot * (_particleMinBound + glm::vec3(d.x, d.y, d.z))
|
||||
};
|
||||
glm::vec3 min(FLT_MAX, FLT_MAX, FLT_MAX);
|
||||
glm::vec3 max = -min;
|
||||
for (size_t i = 0; i < NUM_BOX_CORNERS; i++) {
|
||||
min.x = std::min(min.x, corners[i].x);
|
||||
min.y = std::min(min.y, corners[i].y);
|
||||
min.z = std::min(min.z, corners[i].z);
|
||||
max.x = std::max(max.x, corners[i].x);
|
||||
max.y = std::max(max.y, corners[i].y);
|
||||
max.z = std::max(max.z, corners[i].z);
|
||||
}
|
||||
AABox bound(min, max - min);
|
||||
payload.setBound(bound);
|
||||
|
||||
bool textured = _texture && _texture->isLoaded();
|
||||
if (textured) {
|
||||
payload.setTexture(_texture->getGPUTexture());
|
||||
payload.setPipeline(_texturedPipeline);
|
||||
} else {
|
||||
payload.setTexture(nullptr);
|
||||
payload.setPipeline(_untexturedPipeline);
|
||||
}
|
||||
});
|
||||
|
||||
_scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
||||
void RenderableParticleEffectEntityItem::createPipelines() {
|
||||
if (!_untexturedPipeline) {
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD,
|
||||
gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA,
|
||||
gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(untextured_particle_vert)));
|
||||
auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(untextured_particle_frag)));
|
||||
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader));
|
||||
_untexturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
|
||||
}
|
||||
if (!_texturedPipeline) {
|
||||
auto state = std::make_shared<gpu::State>();
|
||||
state->setCullMode(gpu::State::CULL_BACK);
|
||||
state->setDepthTest(true, true, gpu::LESS_EQUAL);
|
||||
state->setBlendFunction(true, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD,
|
||||
gpu::State::INV_SRC_ALPHA, gpu::State::FACTOR_ALPHA,
|
||||
gpu::State::BLEND_OP_ADD, gpu::State::ONE);
|
||||
auto vertShader = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(textured_particle_vert)));
|
||||
auto fragShader = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(textured_particle_frag)));
|
||||
auto program = gpu::ShaderPointer(gpu::Shader::createProgram(vertShader, fragShader));
|
||||
_texturedPipeline = gpu::PipelinePointer(gpu::Pipeline::create(program, state));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,20 +16,35 @@
|
|||
#include "RenderableEntityItem.h"
|
||||
|
||||
class RenderableParticleEffectEntityItem : public ParticleEffectEntityItem {
|
||||
friend class ParticlePayload;
|
||||
public:
|
||||
static EntityItemPointer factory(const EntityItemID& entityID, const EntityItemProperties& properties);
|
||||
RenderableParticleEffectEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
|
||||
virtual void render(RenderArgs* args);
|
||||
|
||||
void updateQuads(RenderArgs* args, bool textured);
|
||||
virtual void update(const quint64& now) override;
|
||||
|
||||
SIMPLE_RENDERABLE();
|
||||
void updateRenderItem();
|
||||
|
||||
virtual bool addToScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges);
|
||||
virtual void removeFromScene(EntityItemPointer self, render::ScenePointer scene, render::PendingChanges& pendingChanges);
|
||||
|
||||
protected:
|
||||
render::ItemID _renderItemId;
|
||||
|
||||
int _cacheID;
|
||||
const int VERTS_PER_PARTICLE = 4;
|
||||
struct Vertex {
|
||||
Vertex(glm::vec3 xyzIn, glm::vec2 uvIn, uint32_t rgbaIn) : xyz(xyzIn), uv(uvIn), rgba(rgbaIn) {}
|
||||
glm::vec3 xyz;
|
||||
glm::vec2 uv;
|
||||
uint32_t rgba;
|
||||
};
|
||||
|
||||
static void createPipelines();
|
||||
|
||||
std::vector<Vertex> _vertices;
|
||||
static gpu::PipelinePointer _untexturedPipeline;
|
||||
static gpu::PipelinePointer _texturedPipeline;
|
||||
|
||||
render::ScenePointer _scene;
|
||||
NetworkTexturePointer _texture;
|
||||
};
|
||||
|
||||
|
|
20
libraries/entities-renderer/src/textured_particle.slf
Normal file
20
libraries/entities-renderer/src/textured_particle.slf
Normal file
|
@ -0,0 +1,20 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// fragment shader
|
||||
//
|
||||
// Copyright 2015 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
|
||||
//
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
varying vec4 varColor;
|
||||
varying vec2 varTexCoord;
|
||||
|
||||
void main(void) {
|
||||
vec4 color = texture2D(colorMap, varTexCoord);
|
||||
gl_FragColor = color * varColor;
|
||||
}
|
28
libraries/entities-renderer/src/textured_particle.slv
Normal file
28
libraries/entities-renderer/src/textured_particle.slv
Normal file
|
@ -0,0 +1,28 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// particle vertex shader
|
||||
//
|
||||
// Copyright 2015 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 gpu/Transform.slh@>
|
||||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
varying vec4 varColor;
|
||||
varying vec2 varTexCoord;
|
||||
|
||||
void main(void) {
|
||||
// pass along the color & uvs to fragment shader
|
||||
varColor = gl_Color;
|
||||
varTexCoord = gl_MultiTexCoord0.xy;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$>
|
||||
}
|
16
libraries/entities-renderer/src/untextured_particle.slf
Normal file
16
libraries/entities-renderer/src/untextured_particle.slf
Normal file
|
@ -0,0 +1,16 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
// fragment shader
|
||||
//
|
||||
// Copyright 2015 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
|
||||
//
|
||||
|
||||
varying vec4 varColor;
|
||||
|
||||
void main(void) {
|
||||
gl_FragColor = varColor;
|
||||
}
|
24
libraries/entities-renderer/src/untextured_particle.slv
Normal file
24
libraries/entities-renderer/src/untextured_particle.slv
Normal file
|
@ -0,0 +1,24 @@
|
|||
<@include gpu/Config.slh@>
|
||||
<$VERSION_HEADER$>
|
||||
// Generated on <$_SCRIBE_DATE$>
|
||||
//
|
||||
// particle vertex shader
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
<@include gpu/Transform.slh@>
|
||||
|
||||
<$declareStandardTransform()$>
|
||||
|
||||
varying vec4 varColor;
|
||||
|
||||
void main(void) {
|
||||
// pass along the diffuse color
|
||||
varColor = gl_Color;
|
||||
|
||||
TransformCamera cam = getTransformCamera();
|
||||
TransformObject obj = getTransformObject();
|
||||
<$transformModelToClipPos(cam, obj, gl_Vertex, gl_Position)$>
|
||||
}
|
|
@ -39,6 +39,7 @@
|
|||
#include "EntityTree.h"
|
||||
#include "EntityTreeElement.h"
|
||||
#include "EntitiesLogging.h"
|
||||
#include "EntityScriptingInterface.h"
|
||||
#include "ParticleEffectEntityItem.h"
|
||||
|
||||
const xColor ParticleEffectEntityItem::DEFAULT_COLOR = { 255, 255, 255 };
|
||||
|
@ -92,6 +93,75 @@ ParticleEffectEntityItem::ParticleEffectEntityItem(const EntityItemID& entityIte
|
|||
ParticleEffectEntityItem::~ParticleEffectEntityItem() {
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setDimensions(const glm::vec3& value) {
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setLifespan(float lifespan) {
|
||||
_lifespan = lifespan;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setEmitDirection(glm::vec3 emitDirection) {
|
||||
_emitDirection = glm::normalize(emitDirection);
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setEmitStrength(float emitStrength) {
|
||||
_emitStrength = emitStrength;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setLocalGravity(float localGravity) {
|
||||
_localGravity = localGravity;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::setParticleRadius(float particleRadius) {
|
||||
_particleRadius = particleRadius;
|
||||
computeAndUpdateDimensions();
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::computeAndUpdateDimensions() {
|
||||
|
||||
const float t = _lifespan * 1.1f; // add 10% extra time, to account for incremental timer accumulation error.
|
||||
const float MAX_RANDOM_FACTOR = (0.5f * 0.25f);
|
||||
const float maxOffset = (MAX_RANDOM_FACTOR * _emitStrength) + _particleRadius;
|
||||
|
||||
// bounds for x and z is easy to compute because there is no at^2 term.
|
||||
float xMax = (_emitDirection.x * _emitStrength + maxOffset) * t;
|
||||
float xMin = (_emitDirection.x * _emitStrength - maxOffset) * t;
|
||||
|
||||
float zMax = (_emitDirection.z * _emitStrength + maxOffset) * t;
|
||||
float zMin = (_emitDirection.z * _emitStrength - maxOffset) * t;
|
||||
|
||||
// yEnd is where the particle will end.
|
||||
float a = _localGravity;
|
||||
float atSquared = a * t * t;
|
||||
float v = _emitDirection.y * _emitStrength + maxOffset;
|
||||
float vt = v * t;
|
||||
float yEnd = 0.5f * atSquared + vt;
|
||||
|
||||
// yApex is where the particle is at it's apex.
|
||||
float yApexT = (-v / a);
|
||||
float yApex = 0.0f;
|
||||
|
||||
// only set apex if it's within the lifespan of the particle.
|
||||
if (yApexT >= 0.0f && yApexT <= t) {
|
||||
yApex = -(v * v) / (2.0f * a);
|
||||
}
|
||||
|
||||
float yMax = std::max(yApex, yEnd);
|
||||
float yMin = std::min(yApex, yEnd);
|
||||
|
||||
// times 2 because dimensions are diameters not radii.
|
||||
glm::vec3 dims(2.0f * std::max(fabsf(xMin), fabsf(xMax)),
|
||||
2.0f * std::max(fabsf(yMin), fabsf(yMax)),
|
||||
2.0f * std::max(fabsf(zMin), fabsf(zMax)));
|
||||
|
||||
EntityItem::setDimensions(dims);
|
||||
}
|
||||
|
||||
EntityItemProperties ParticleEffectEntityItem::getProperties() const {
|
||||
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
|
||||
|
||||
|
@ -245,7 +315,7 @@ bool ParticleEffectEntityItem::isAnimatingSomething() const {
|
|||
}
|
||||
|
||||
bool ParticleEffectEntityItem::needsToCallUpdate() const {
|
||||
return isAnimatingSomething() ? true : EntityItem::needsToCallUpdate();
|
||||
return true;
|
||||
}
|
||||
|
||||
void ParticleEffectEntityItem::update(const quint64& now) {
|
||||
|
@ -260,13 +330,6 @@ void ParticleEffectEntityItem::update(const quint64& now) {
|
|||
|
||||
if (isAnimatingSomething()) {
|
||||
stepSimulation(deltaTime);
|
||||
|
||||
// update the dimensions
|
||||
glm::vec3 dims;
|
||||
dims.x = glm::max(glm::abs(_particleMinBound.x), glm::abs(_particleMaxBound.x)) * 2.0f;
|
||||
dims.y = glm::max(glm::abs(_particleMinBound.y), glm::abs(_particleMaxBound.y)) * 2.0f;
|
||||
dims.z = glm::max(glm::abs(_particleMinBound.z), glm::abs(_particleMaxBound.z)) * 2.0f;
|
||||
setDimensions(dims);
|
||||
}
|
||||
|
||||
EntityItem::update(now); // let our base class handle it's updates...
|
||||
|
@ -319,7 +382,7 @@ void ParticleEffectEntityItem::setAnimationSettings(const QString& value) {
|
|||
qCDebug(entities) << "ParticleEffectEntityItem::setAnimationSettings() calling setAnimationFrameIndex()...";
|
||||
qCDebug(entities) << " settings:" << value;
|
||||
qCDebug(entities) << " settingsMap[frameIndex]:" << settingsMap["frameIndex"];
|
||||
qCDebug(entities" frameIndex: %20.5f", frameIndex);
|
||||
qCDebug(entities, " frameIndex: %20.5f", frameIndex);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -86,12 +86,14 @@ public:
|
|||
void setAnimationLastFrame(float lastFrame) { _animationLoop.setLastFrame(lastFrame); }
|
||||
float getAnimationLastFrame() const { return _animationLoop.getLastFrame(); }
|
||||
|
||||
virtual void setDimensions(const glm::vec3& value) override;
|
||||
|
||||
static const quint32 DEFAULT_MAX_PARTICLES;
|
||||
void setMaxParticles(quint32 maxParticles);
|
||||
quint32 getMaxParticles() const { return _maxParticles; }
|
||||
|
||||
static const float DEFAULT_LIFESPAN;
|
||||
void setLifespan(float lifespan) { _lifespan = lifespan; }
|
||||
void setLifespan(float lifespan);
|
||||
float getLifespan() const { return _lifespan; }
|
||||
|
||||
static const float DEFAULT_EMIT_RATE;
|
||||
|
@ -99,21 +101,23 @@ public:
|
|||
float getEmitRate() const { return _emitRate; }
|
||||
|
||||
static const glm::vec3 DEFAULT_EMIT_DIRECTION;
|
||||
void setEmitDirection(glm::vec3 emitDirection) { _emitDirection = glm::normalize(emitDirection); }
|
||||
void setEmitDirection(glm::vec3 emitDirection);
|
||||
const glm::vec3& getEmitDirection() const { return _emitDirection; }
|
||||
|
||||
static const float DEFAULT_EMIT_STRENGTH;
|
||||
void setEmitStrength(float emitStrength) { _emitStrength = emitStrength; }
|
||||
void setEmitStrength(float emitStrength);
|
||||
float getEmitStrength() const { return _emitStrength; }
|
||||
|
||||
static const float DEFAULT_LOCAL_GRAVITY;
|
||||
void setLocalGravity(float localGravity) { _localGravity = localGravity; }
|
||||
void setLocalGravity(float localGravity);
|
||||
float getLocalGravity() const { return _localGravity; }
|
||||
|
||||
static const float DEFAULT_PARTICLE_RADIUS;
|
||||
void setParticleRadius(float particleRadius) { _particleRadius = particleRadius; }
|
||||
void setParticleRadius(float particleRadius);
|
||||
float getParticleRadius() const { return _particleRadius; }
|
||||
|
||||
void computeAndUpdateDimensions();
|
||||
|
||||
bool getAnimationIsPlaying() const { return _animationLoop.isRunning(); }
|
||||
float getAnimationFrameIndex() const { return _animationLoop.getFrameIndex(); }
|
||||
float getAnimationFPS() const { return _animationLoop.getFPS(); }
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
#include <PathUtils.h>
|
||||
#include <PerfStat.h>
|
||||
#include "PhysicsEntity.h"
|
||||
#include <ShapeCollider.h>
|
||||
#include <SphereShape.h>
|
||||
#include <ViewFrustum.h>
|
||||
|
||||
#include "AbstractViewStateInterface.h"
|
||||
|
@ -223,10 +221,6 @@ void Model::setScaleInternal(const glm::vec3& scale) {
|
|||
if (relativeDeltaScale > ONE_PERCENT || scaleLength < EPSILON) {
|
||||
_scale = scale;
|
||||
initJointTransforms();
|
||||
if (_shapes.size() > 0) {
|
||||
clearShapes();
|
||||
buildShapes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1165,15 +1159,6 @@ QStringList Model::getJointNames() const {
|
|||
return isActive() ? _geometry->getFBXGeometry().getJointNames() : QStringList();
|
||||
}
|
||||
|
||||
// virtual override from PhysicsEntity
|
||||
void Model::buildShapes() {
|
||||
// TODO: figure out how to load/build collision shapes for general models
|
||||
}
|
||||
|
||||
void Model::updateShapePositions() {
|
||||
// TODO: implement this when we know how to build shapes for regular Models
|
||||
}
|
||||
|
||||
class Blender : public QRunnable {
|
||||
public:
|
||||
|
||||
|
@ -1342,9 +1327,7 @@ void Model::simulateInternal(float deltaTime) {
|
|||
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
_rig->simulateInternal(deltaTime, parentTransform, getTranslation(), getRotation());
|
||||
|
||||
_shapesAreDirty = !_shapes.isEmpty();
|
||||
updateRig(deltaTime, parentTransform);
|
||||
|
||||
glm::mat4 modelToWorld = glm::mat4_cast(_rotation);
|
||||
for (int i = 0; i < _meshStates.size(); i++) {
|
||||
|
@ -1385,7 +1368,6 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl
|
|||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
if (_rig->setJointPosition(jointIndex, position, rotation, useRotation,
|
||||
lastFreeIndex, allIntermediatesFree, alignment, priority, freeLineage, parentTransform)) {
|
||||
_shapesAreDirty = !_shapes.isEmpty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -1396,7 +1378,6 @@ void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm:
|
|||
const QVector<int>& freeLineage = geometry.joints.at(endIndex).freeLineage;
|
||||
glm::mat4 parentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset;
|
||||
_rig->inverseKinematics(endIndex, targetPosition, targetRotation, priority, freeLineage, parentTransform);
|
||||
_shapesAreDirty = !_shapes.isEmpty();
|
||||
}
|
||||
|
||||
bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) {
|
||||
|
@ -1411,10 +1392,6 @@ float Model::getLimbLength(int jointIndex) const {
|
|||
return _rig->getLimbLength(jointIndex, freeLineage, _scale, geometry.joints);
|
||||
}
|
||||
|
||||
void Model::renderJointCollisionShapes(float alpha) {
|
||||
// implement this when we have shapes for regular models
|
||||
}
|
||||
|
||||
bool Model::maybeStartBlender() {
|
||||
const FBXGeometry& fbxGeometry = _geometry->getFBXGeometry();
|
||||
if (fbxGeometry.hasBlendedMeshes()) {
|
||||
|
@ -1480,7 +1457,6 @@ void Model::deleteGeometry() {
|
|||
_blendedVertexBuffers.clear();
|
||||
_rig->clearJointStates();
|
||||
_meshStates.clear();
|
||||
clearShapes();
|
||||
|
||||
_rig->deleteAnimations();
|
||||
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
class AbstractViewStateInterface;
|
||||
class QScriptEngine;
|
||||
|
||||
class Shape;
|
||||
#include "RenderArgs.h"
|
||||
class ViewFrustum;
|
||||
|
||||
|
@ -89,7 +88,6 @@ public:
|
|||
void removeFromScene(std::shared_ptr<render::Scene> scene, render::PendingChanges& pendingChanges);
|
||||
void renderSetup(RenderArgs* args);
|
||||
bool isRenderable() const { return !_meshStates.isEmpty() || (isActive() && _geometry->getMeshes().isEmpty()); }
|
||||
virtual void renderJointCollisionShapes(float alpha);
|
||||
|
||||
bool isVisible() const { return _isVisible; }
|
||||
|
||||
|
@ -229,10 +227,6 @@ protected:
|
|||
/// \return true if joint exists
|
||||
bool getJointPosition(int jointIndex, glm::vec3& position) const;
|
||||
|
||||
// virtual overrides from PhysicsEntity
|
||||
virtual void buildShapes();
|
||||
virtual void updateShapePositions();
|
||||
|
||||
void setShowTrueJointTransforms(bool show) { _showTrueJointTransforms = show; }
|
||||
|
||||
QSharedPointer<NetworkGeometry> _geometry;
|
||||
|
@ -270,6 +264,7 @@ protected:
|
|||
void snapToRegistrationPoint();
|
||||
|
||||
void simulateInternal(float deltaTime);
|
||||
virtual void updateRig(float deltaTime, glm::mat4 parentTransform) {}; // Subclasses may be more interesting
|
||||
|
||||
/// Updates the state of the joint at the specified index.
|
||||
virtual void updateJointState(int index);
|
||||
|
|
|
@ -11,17 +11,10 @@
|
|||
|
||||
#include "PhysicsEntity.h"
|
||||
|
||||
#include "PlaneShape.h"
|
||||
#include "Shape.h"
|
||||
#include "ShapeCollider.h"
|
||||
#include "SphereShape.h"
|
||||
|
||||
PhysicsEntity::PhysicsEntity() :
|
||||
_translation(0.0f),
|
||||
_rotation(),
|
||||
_boundingRadius(0.0f),
|
||||
_shapesAreDirty(true),
|
||||
_enableShapes(false) {
|
||||
_boundingRadius(0.0f) {
|
||||
}
|
||||
|
||||
PhysicsEntity::~PhysicsEntity() {
|
||||
|
@ -29,143 +22,13 @@ PhysicsEntity::~PhysicsEntity() {
|
|||
|
||||
void PhysicsEntity::setTranslation(const glm::vec3& translation) {
|
||||
if (_translation != translation) {
|
||||
_shapesAreDirty = !_shapes.isEmpty();
|
||||
_translation = translation;
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEntity::setRotation(const glm::quat& rotation) {
|
||||
if (_rotation != rotation) {
|
||||
_shapesAreDirty = !_shapes.isEmpty();
|
||||
_rotation = rotation;
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEntity::setShapeBackPointers() {
|
||||
for (int i = 0; i < _shapes.size(); i++) {
|
||||
Shape* shape = _shapes[i];
|
||||
if (shape) {
|
||||
shape->setEntity(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEntity::setEnableShapes(bool enable) {
|
||||
if (enable != _enableShapes) {
|
||||
clearShapes();
|
||||
_enableShapes = enable;
|
||||
if (_enableShapes) {
|
||||
buildShapes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicsEntity::clearShapes() {
|
||||
for (int i = 0; i < _shapes.size(); ++i) {
|
||||
delete _shapes[i];
|
||||
}
|
||||
_shapes.clear();
|
||||
}
|
||||
|
||||
bool PhysicsEntity::findRayIntersection(RayIntersectionInfo& intersection) const {
|
||||
return ShapeCollider::findRayIntersection(_shapes, intersection);
|
||||
}
|
||||
|
||||
bool PhysicsEntity::findCollisions(const QVector<const Shape*> shapes, CollisionList& collisions) {
|
||||
bool collided = false;
|
||||
int numTheirShapes = shapes.size();
|
||||
for (int i = 0; i < numTheirShapes; ++i) {
|
||||
const Shape* theirShape = shapes[i];
|
||||
if (!theirShape) {
|
||||
continue;
|
||||
}
|
||||
int numOurShapes = _shapes.size();
|
||||
for (int j = 0; j < numOurShapes; ++j) {
|
||||
const Shape* ourShape = _shapes.at(j);
|
||||
if (ourShape && ShapeCollider::collideShapes(theirShape, ourShape, collisions)) {
|
||||
collided = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return collided;
|
||||
}
|
||||
|
||||
bool PhysicsEntity::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions) {
|
||||
bool collided = false;
|
||||
SphereShape sphere(sphereRadius, sphereCenter);
|
||||
for (int i = 0; i < _shapes.size(); i++) {
|
||||
Shape* shape = _shapes[i];
|
||||
if (!shape) {
|
||||
continue;
|
||||
}
|
||||
if (ShapeCollider::collideShapes(&sphere, shape, collisions)) {
|
||||
CollisionInfo* collision = collisions.getLastCollision();
|
||||
collision->_data = (void*)(this);
|
||||
collision->_intData = i;
|
||||
collided = true;
|
||||
}
|
||||
}
|
||||
return collided;
|
||||
}
|
||||
|
||||
bool PhysicsEntity::findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions) {
|
||||
bool collided = false;
|
||||
PlaneShape planeShape(plane);
|
||||
for (int i = 0; i < _shapes.size(); i++) {
|
||||
if (_shapes.at(i) && ShapeCollider::collideShapes(&planeShape, _shapes.at(i), collisions)) {
|
||||
CollisionInfo* collision = collisions.getLastCollision();
|
||||
collision->_data = (void*)(this);
|
||||
collision->_intData = i;
|
||||
collided = true;
|
||||
}
|
||||
}
|
||||
return collided;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// TODO: enforce this maximum when shapes are actually built. The gotcha here is
|
||||
// that the Model class (derived from PhysicsEntity) expects numShapes == numJoints,
|
||||
// so we have to modify that code to be safe.
|
||||
const int MAX_SHAPES_PER_ENTITY = 256;
|
||||
|
||||
// the first 256 prime numbers
|
||||
const int primes[256] = {
|
||||
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
|
||||
31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
|
||||
73, 79, 83, 89, 97, 101, 103, 107, 109, 113,
|
||||
127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
|
||||
179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
|
||||
233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
|
||||
283, 293, 307, 311, 313, 317, 331, 337, 347, 349,
|
||||
353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
|
||||
419, 421, 431, 433, 439, 443, 449, 457, 461, 463,
|
||||
467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
|
||||
547, 557, 563, 569, 571, 577, 587, 593, 599, 601,
|
||||
607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
|
||||
661, 673, 677, 683, 691, 701, 709, 719, 727, 733,
|
||||
739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
|
||||
811, 821, 823, 827, 829, 839, 853, 857, 859, 863,
|
||||
877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
|
||||
947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013,
|
||||
1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069,
|
||||
1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151,
|
||||
1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223,
|
||||
1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291,
|
||||
1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373,
|
||||
1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451,
|
||||
1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511,
|
||||
1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583,
|
||||
1597, 1601, 1607, 1609, 1613, 1619 };
|
||||
|
||||
void PhysicsEntity::disableCollisions(int shapeIndexA, int shapeIndexB) {
|
||||
if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) {
|
||||
_disabledCollisions.insert(primes[shapeIndexA] * primes[shapeIndexB]);
|
||||
}
|
||||
}
|
||||
|
||||
bool PhysicsEntity::collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const {
|
||||
if (shapeIndexA < MAX_SHAPES_PER_ENTITY && shapeIndexB < MAX_SHAPES_PER_ENTITY) {
|
||||
return !_disabledCollisions.contains(primes[shapeIndexA] * primes[shapeIndexB]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@
|
|||
#include <CollisionInfo.h>
|
||||
#include <RayIntersectionInfo.h>
|
||||
|
||||
class Shape;
|
||||
|
||||
class PhysicsEntity {
|
||||
|
||||
public:
|
||||
|
@ -38,30 +36,10 @@ public:
|
|||
const glm::quat& getRotation() const { return _rotation; }
|
||||
float getBoundingRadius() const { return _boundingRadius; }
|
||||
|
||||
void setShapeBackPointers();
|
||||
|
||||
void setEnableShapes(bool enable);
|
||||
|
||||
virtual void buildShapes() = 0;
|
||||
virtual void clearShapes();
|
||||
const QVector<Shape*> getShapes() const { return _shapes; }
|
||||
|
||||
bool findRayIntersection(RayIntersectionInfo& intersection) const;
|
||||
bool findCollisions(const QVector<const Shape*> shapes, CollisionList& collisions);
|
||||
bool findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadius, CollisionList& collisions);
|
||||
bool findPlaneCollisions(const glm::vec4& plane, CollisionList& collisions);
|
||||
|
||||
void disableCollisions(int shapeIndexA, int shapeIndexB);
|
||||
bool collisionsAreEnabled(int shapeIndexA, int shapeIndexB) const;
|
||||
|
||||
protected:
|
||||
glm::vec3 _translation;
|
||||
glm::quat _rotation;
|
||||
float _boundingRadius;
|
||||
bool _shapesAreDirty;
|
||||
bool _enableShapes;
|
||||
QVector<Shape*> _shapes;
|
||||
QSet<int> _disabledCollisions;
|
||||
};
|
||||
|
||||
#endif // hifi_PhysicsEntity_h
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Declare dependencies
|
||||
macro (setup_testcase_dependencies)
|
||||
# link in the shared libraries
|
||||
link_hifi_libraries(shared animation gpu fbx model avatars networking audio)
|
||||
link_hifi_libraries(shared animation gpu fbx model)
|
||||
|
||||
copy_dlls_beside_windows_executable()
|
||||
endmacro ()
|
|
@ -40,11 +40,9 @@
|
|||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <PathUtils.h>
|
||||
|
||||
#include "AvatarData.h"
|
||||
#include "OBJReader.h"
|
||||
#include "FBXReader.h"
|
||||
#include "OBJReader.h"
|
||||
|
||||
#include "AvatarRig.h" // We might later test Rig vs AvatarRig separately, but for now, we're concentrating on the main use case.
|
||||
#include "RigTests.h"
|
||||
|
@ -52,24 +50,7 @@
|
|||
QTEST_MAIN(RigTests)
|
||||
|
||||
void RigTests::initTestCase() {
|
||||
|
||||
// There are two good ways we could organize this:
|
||||
// 1. Create a MyAvatar the same way that Interface does, and poke at it.
|
||||
// We can't do that because MyAvatar (and even Avatar) are in interface, not a library, and our build system won't allow that dependency.
|
||||
// 2. Create just the minimum skeleton in the most direct way possible, using only very basic library APIs (such as fbx).
|
||||
// I don't think we can do that because not everything we need is exposed directly from, e.g., the fst and fbx readers.
|
||||
// So here we do neither. Using as much as we can from AvatarData (which is in the avatar and further requires network and audio), and
|
||||
// duplicating whatever other code we need from (My)Avatar. Ugh. We may refactor that later, but right now, cleaning this up is not on our critical path.
|
||||
|
||||
// Joint mapping from fst. FIXME: Do we need this???
|
||||
/*auto avatar = std::make_shared<AvatarData>();
|
||||
QEventLoop loop; // Create an event loop that will quit when we get the finished signal
|
||||
QObject::connect(avatar.get(), &AvatarData::jointMappingLoaded, &loop, &QEventLoop::quit);
|
||||
avatar->setSkeletonModelURL(QUrl("https://hifi-public.s3.amazonaws.com/marketplace/contents/4a690585-3fa3-499e-9f8b-fd1226e561b1/e47e6898027aa40f1beb6adecc6a7db5.fst")); // Zach fst
|
||||
loop.exec();*/ // Blocking all further tests until signalled.
|
||||
|
||||
// Joint geometry from fbx.
|
||||
#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx"
|
||||
//#define FROM_FILE "/Users/howardstearns/howardHiFi/Zack.fbx"
|
||||
#ifdef FROM_FILE
|
||||
QFile file(FROM_FILE);
|
||||
QCOMPARE(file.open(QIODevice::ReadOnly), true);
|
||||
|
@ -81,8 +62,7 @@ void RigTests::initTestCase() {
|
|||
QCOMPARE(fbxHttpCode, 200);
|
||||
FBXGeometry geometry = readFBX(reply->readAll(), QVariantHash());
|
||||
#endif
|
||||
//QCOMPARE(geometry.joints.count(), avatar->getJointNames().count());
|
||||
|
||||
|
||||
QVector<JointState> jointStates;
|
||||
for (int i = 0; i < geometry.joints.size(); ++i) {
|
||||
// Note that if the geometry is stack allocated and goes away, so will the joints. Hence the heap copy here.
|
||||
|
@ -97,24 +77,24 @@ void RigTests::initTestCase() {
|
|||
std::cout << "Rig is ready " << geometry.joints.count() << " joints " << std::endl;
|
||||
}
|
||||
|
||||
void reportJoint(int index, JointState joint) { // Handy for debugging
|
||||
static void reportJoint(int index, JointState joint) { // Handy for debugging
|
||||
std::cout << "\n";
|
||||
std::cout << index << " " << joint.getFBXJoint().name.toUtf8().data() << "\n";
|
||||
std::cout << " pos:" << joint.getPosition() << "/" << joint.getPositionInParentFrame() << " from " << joint.getParentIndex() << "\n";
|
||||
std::cout << " rot:" << safeEulerAngles(joint.getRotation()) << "/" << safeEulerAngles(joint.getRotationInParentFrame()) << "/" << safeEulerAngles(joint.getRotationInBindFrame()) << "\n";
|
||||
std::cout << "\n";
|
||||
}
|
||||
void reportByName(RigPointer rig, const QString& name) {
|
||||
static void reportByName(RigPointer rig, const QString& name) {
|
||||
int jointIndex = rig->indexOfJoint(name);
|
||||
reportJoint(jointIndex, rig->getJointState(jointIndex));
|
||||
}
|
||||
void reportAll(RigPointer rig) {
|
||||
static void reportAll(RigPointer rig) {
|
||||
for (int i = 0; i < rig->getJointStateCount(); i++) {
|
||||
JointState joint = rig->getJointState(i);
|
||||
reportJoint(i, joint);
|
||||
}
|
||||
}
|
||||
void reportSome(RigPointer rig) {
|
||||
static void reportSome(RigPointer rig) {
|
||||
QString names[] = {"Head", "Neck", "RightShoulder", "RightArm", "RightForeArm", "RightHand", "Spine2", "Spine1", "Spine", "Hips", "RightUpLeg", "RightLeg", "RightFoot", "RightToeBase", "RightToe_End"};
|
||||
for (auto name : names) {
|
||||
reportByName(rig, name);
|
Loading…
Reference in a new issue