diff --git a/Client/App/App.vcproj b/Client/App/App.vcproj index 813b5c2f..7e86a783 100644 --- a/Client/App/App.vcproj +++ b/Client/App/App.vcproj @@ -804,11 +804,11 @@ - - + - + @@ -1286,6 +1286,10 @@ RelativePath=".\v8datamodel\BrickColor.cpp" > + + diff --git a/Client/App/include/util/Extents.h b/Client/App/include/util/Extents.h index 8e28bb5f..b711c765 100644 --- a/Client/App/include/util/Extents.h +++ b/Client/App/include/util/Extents.h @@ -42,7 +42,11 @@ namespace RBX NormalId closestFace(const G3D::Vector3& point); void unionWith(const Extents& other); void shift(const G3D::Vector3&); - void scale(float); + void scale(float scale) + { + this->low *= scale; + this->high *= scale; + } void expand(float f) { this->low -= G3D::Vector3(f, f, f); diff --git a/Client/App/include/v8datamodel/Camera.h b/Client/App/include/v8datamodel/Camera.h index 0651b621..6e3c8e12 100644 --- a/Client/App/include/v8datamodel/Camera.h +++ b/Client/App/include/v8datamodel/Camera.h @@ -1,9 +1,9 @@ #pragma once -#include "v8tree/Instance.h" #include #include #include #include +#include "v8tree/Instance.h" #include "util/Extents.h" namespace RBX @@ -54,17 +54,17 @@ namespace RBX void updateFocus(); void updateGoal(); bool characterZoom(float); - bool nonCharacterZoom(float); - void tryZoomExtents(float, float, float, const Extents&, const G3D::Rect2D&); + bool nonCharacterZoom(float in); + void tryZoomExtents(float low, float current, float high, const RBX::Extents& extents, const G3D::Rect2D& viewPort); ContactManager& getContactManager(); float goalToFocusDistance() const; void setGCameraCoordinateFrame(const G3D::CoordinateFrame&); G3D::CoordinateFrame computeLineOfSiteGoal(); - void getHeadingElevationDistance(float&, float&, float&); + void getHeadingElevationDistance(float& heading, float& elevation, float& distance); void setHeadingElevationDistance(float, float, float); void tellCameraMoved(); void getIgnorePrims(G3D::Array&); - virtual bool askSetParent(const Instance*) const; + virtual bool askSetParent(const Instance* instance) const; public: //Camera(const Camera&); Camera(); @@ -81,29 +81,32 @@ namespace RBX bool isFirstPersonCamera() const; ICameraSubject* getCameraSubject() const; Instance* getCameraSubjectInstance() const; - void setCameraSubject(Instance*); - const G3D::CoordinateFrame& getCameraFocus() const; - void setCameraFocus(const G3D::CoordinateFrame&); + void setCameraSubject(Instance* newSubject); + const G3D::CoordinateFrame& getCameraFocus() const + { + return cameraFocus; + } + void setCameraFocus(const G3D::CoordinateFrame& value); G3D::CoordinateFrame getCameraCoordinateFrame() const { return gCamera.getCoordinateFrame(); } - void setCameraCoordinateFrameNoLerp(const G3D::CoordinateFrame&); + void setCameraCoordinateFrameNoLerp(const G3D::CoordinateFrame& value); void goalToCamera(); CameraType getCameraType() const; - void setCameraType(CameraType); + void setCameraType(CameraType type); bool canZoom(int) const; bool canTilt(int) const; void onWrapMouse(const G3D::Vector2&); - bool zoom(float); - bool setDistanceFromTarget(float); - void zoomExtents(Extents, const G3D::Rect2D&, ZoomType); - bool zoomExtents(const G3D::Rect2D&); - void panRadians(float); + bool zoom(float in); + bool setDistanceFromTarget(float newDistance); + void zoomExtents(Extents extents, const G3D::Rect2D& viewPort, ZoomType zoomType); + bool zoomExtents(const G3D::Rect2D& viewPort); + void panRadians(float angle); void panUnits(int); bool tiltRadians(float); bool tiltUnits(int); - void lookAt(const G3D::Vector3&); + void lookAt(const G3D::Vector3& point); void setImageServerViewNoLerp(const G3D::CoordinateFrame&, const G3D::Rect2D&); public: //Camera& operator=(const Camera&); diff --git a/Client/App/v8datamodel/Camera.cpp b/Client/App/v8datamodel/Camera.cpp new file mode 100644 index 00000000..0bddea9d --- /dev/null +++ b/Client/App/v8datamodel/Camera.cpp @@ -0,0 +1,446 @@ +#include "v8datamodel/Camera.h" +#include "v8datamodel/Workspace.h" +#include "v8datamodel/ICharacterSubject.h" +#include "util/Math.h" + +namespace RBX +{ + const char* sCamera = "Camera"; + + static const Reflection::EnumPropDescriptor desc_cameraType("CameraType", "Camera", &Camera::getCameraType, &Camera::setCameraType, Reflection::PropertyDescriptor::STANDARD); + static const Reflection::PropDescriptor desc_CoordFrame("CoordinateFrame", "Data", &Camera::getCameraCoordinateFrame, &Camera::setCameraCoordinateFrameNoLerp, Reflection::PropertyDescriptor::STREAMING); + static const Reflection::PropDescriptor desc_Focus("Focus", "Data", &Camera::getCameraFocus, &Camera::setCameraFocus, Reflection::PropertyDescriptor::STREAMING); + static const Reflection::RefPropDescriptor cameraSubjectProp("CameraSubject", "Camera", &Camera::getCameraSubjectInstance, &Camera::setCameraSubject, Reflection::PropertyDescriptor::STANDARD); + + Camera::Camera() + : Base(), + cameraFocus(G3D::Vector3(0, 0, -5)), + cameraType(FIXED_CAMERA), + animationType(AUTO), + cameraExternallyAdjusted(false) + { + setName("Camera"); + gCamera.setNearPlaneZ(1.25); + gCamera.setFarPlaneZ(5000); + gCamera.setFieldOfView(G3D::toRadians(60)); + + G3D::CoordinateFrame cameraCoord(G3D::Vector3(0, 5, 5)); + cameraCoord.lookAt(G3D::Vector3::zero()); + + if (Math::legalCameraCoord(cameraCoord)) + { + gCamera.setCoordinateFrame(cameraCoord); + } + else + { + RBXASSERT(0); + } + } + + Camera::~Camera() + { + } + + bool Camera::askSetParent(const Instance* instance) const + { + return fastDynamicCast(instance) != NULL; + } + + ICameraOwner* Camera::getCameraOwner() + { + Instance* instance = getParent(); + while (instance != NULL) + { + ICameraOwner* cameraOwner = fastDynamicCast(instance); + if (cameraOwner) + return cameraOwner; + + instance = getParent(); + } + + return NULL; + } + + void Camera::lookAt(const G3D::Vector3& point) + { + cameraFocus.translation = point; + cameraGoal.lookAt(point); + } + + void Camera::getHeadingElevationDistance(float& heading, float& elevation, float& distance) + { + Math::getHeadingElevation(cameraGoal, heading, elevation); + distance = (cameraGoal.translation - cameraFocus.translation).magnitude(); + } + + //70.53% matching. + bool Camera::setDistanceFromTarget(float newDistance) + { + G3D::Vector3 lookVector = cameraFocus.translation - cameraGoal.translation; + float currentDistance = lookVector.magnitude(); + + if (newDistance < 0.5 && currentDistance == 0.5 || newDistance > 1000.0 && currentDistance == 1000.0) + return false; + + newDistance = (newDistance < 0.5) ? 0.5 : newDistance; + newDistance = (newDistance > 1000.0) ? 1000.0 : newDistance; + + lookVector *= newDistance; + cameraGoal.translation = cameraFocus.translation - (lookVector / currentDistance); + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + + return true; + } + + void Camera::alwaysMode() + { + animationType = ALWAYS; + } + + bool Camera::nonCharacterZoom(float in) + { + G3D::Vector3 lookVector = cameraFocus.translation - cameraGoal.translation; + + float currentDistance = lookVector.magnitude(); + float newZoomDistance = getNewZoomDistance(currentDistance, in); + + if (currentDistance == newZoomDistance) + return false; + + cameraGoal.translation -= lookVector * (newZoomDistance / currentDistance - 1.0f); + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + + return true; + } + + void Camera::setHeadingElevationDistance(float heading, float elevation, float distance) + { + Math::setHeadingElevation(cameraGoal, heading, elevation); + cameraFocus.rotation = cameraGoal.rotation; + + G3D::Vector3 lookVector = cameraFocus.lookVector(); + cameraGoal.translation = cameraFocus.translation - lookVector * distance; + cameraExternallyAdjusted = true; + } + + void Camera::panRadians(float angle) + { + RBXASSERT(angle > -100); + RBXASSERT(angle < 100); + + if (angle != 0) + { + float heading, elevation, distance; + + getHeadingElevationDistance(heading, elevation, distance); + heading = Math::radWrap(heading + angle); + setHeadingElevationDistance(heading, elevation, distance); + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + + void Camera::updateFocus() + { + cameraFocus = getCameraSubject()->getLocation(); + } + + //87.80% matching. + void Camera::updateGoal() + { + switch(cameraType) + { + case WATCH_CAMERA: + { + updateFocus(); + break; + } + case ATTACH_CAMERA: + { + G3D::Vector3 v1 = cameraGoal.translation - cameraFocus.translation; + double distance = v1.xz().length(); + + updateFocus(); + + G3D::Vector2 direction = cameraFocus.lookVector().xz().direction(); + + cameraGoal.translation = G3D::Vector3(cameraFocus.translation.x - direction.x * distance, + cameraFocus.translation.y + v1.y, + cameraFocus.translation.z - direction.y * distance); + break; + } + case TRACK_CAMERA: + { + G3D::Vector3 v1 = cameraFocus.translation; + + updateFocus(); + + cameraGoal.translation += cameraFocus.translation - v1; + break; + } + case FOLLOW_CAMERA: + { + G3D::Vector3 v1 = cameraFocus.translation - cameraGoal.translation; + double distance = v1.xz().length(); + + updateFocus(); + + G3D::Vector2 direction = (cameraGoal.translation.xz() - cameraFocus.translation.xz()).direction(); + + cameraGoal.translation = G3D::Vector3(cameraFocus.translation.x - direction.x * distance, + cameraFocus.translation.y - v1.y, + cameraFocus.translation.z - direction.y * distance); + + break; + } + case CUSTOM_CAMERA: + { + ICameraSubject* cameraSubject = getCameraSubject(); + if(cameraSubject) + cameraSubject->stepGoalAndFocus(cameraGoal, cameraFocus, cameraExternallyAdjusted); + cameraExternallyAdjusted = false; + break; + } + } + cameraGoal.lookAt(cameraFocus.translation); + } + + bool Camera::zoom(float in) + { + if (cameraType == CUSTOM_CAMERA) + { + ICameraSubject* cameraSubject = getCameraSubject(); + if (cameraSubject) + return cameraSubject->zoom(in, cameraGoal, cameraFocus); + } + else if (getCameraSubjectInstance() && + (cameraType == FOLLOW_CAMERA || + cameraType == ATTACH_CAMERA || + cameraType == TRACK_CAMERA)) + { + return characterZoom(in); + } + else + { + return nonCharacterZoom(in); + } + + return false; + } + + void Camera::onHeartbeat() + { + updateGoal(); + G3D::CoordinateFrame adjustedGoal = cameraGoal; + ICameraSubject* cameraSubject = getCameraSubject(); + ICharacterSubject* characterSubject = dynamic_cast(cameraSubject); + + if (characterSubject) + characterSubject->onHeartBeat(cameraGoal, cameraFocus); + + G3D::CoordinateFrame cameraCoord = gCamera.getCoordinateFrame(); + G3D::CoordinateFrame LerpFrame = cameraCoord.lerp(adjustedGoal, 0.9f); + + if (Math::legalCameraCoord(LerpFrame)) + { + gCamera.setCoordinateFrame(LerpFrame); + } + else + { + RBXASSERT(0); + } + + if (animationType == ALWAYS || !Math::fuzzyEq(LerpFrame, cameraCoord, 0.01f, 0.01f)) + { + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + + Instance* Camera::getCameraSubjectInstance() const + { + return cameraSubject.get(); + } + + ICameraSubject* Camera::getCameraSubject() const + { + Instance* instance = getCameraSubjectInstance(); + if (instance) + { + ICameraSubject* subject = fastDynamicCast(instance); + RBXASSERT(subject); + return subject; + } + return NULL; + } + + void Camera::autoMode() + { + if (animationType != AUTO) + { + animationType = AUTO; + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + + void Camera::setCameraType(CameraType type) + { + if (cameraType != type) + { + cameraType = type; + raisePropertyChanged(desc_cameraType); + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + + void Camera::setCameraSubject(Instance* newSubject) + { + if (newSubject != getCameraSubjectInstance()) + { + if (fastDynamicCast(newSubject)) + { + cameraSubject = shared_from((ModelInstance*) newSubject); + raisePropertyChanged(cameraSubjectProp); + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + } + + void Camera::setCameraFocus(const G3D::CoordinateFrame& value) + { + if (value != cameraFocus) + { + cameraFocus = value; + raisePropertyChanged(desc_Focus); + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + + void Camera::goalToCamera() + { + if (gCamera.getCoordinateFrame() != cameraGoal) + { + cameraExternallyAdjusted = true; + if (Math::legalCameraCoord(cameraGoal)) + { + gCamera.setCoordinateFrame(cameraGoal); + } + else + { + RBXASSERT(0); + } + raisePropertyChanged(desc_CoordFrame); + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + + //84.84% matching. + void Camera::tryZoomExtents(float low, float current, float high, const RBX::Extents& extents, const G3D::Rect2D& viewPort) + { + RBXASSERT(current >= low); + RBXASSERT(current <= high); + + if (high - low > 0.1) + { + setDistanceFromTarget(current); + updateGoal(); + goalToCamera(); + + bool isContained = extents.containedByFrustum(gCamera.frustum(viewPort)); + + float newLow = (isContained) ? low : current; + float newHigh = (isContained) ? current : high; + float newCurrent = (newHigh + newLow) * 0.5; + + tryZoomExtents(newLow, newCurrent, newHigh, extents, viewPort); + } + } + + //87.82% matching. + void Camera::zoomExtents(Extents extents, const G3D::Rect2D& viewPort, Camera::ZoomType zoomType) + { + G3D::CoordinateFrame currentCoord = gCamera.getCoordinateFrame(); + extents.expand(0.1f); + + if (zoomType != ZOOM_CHAR_PART_DRAG || cameraType == CUSTOM_CAMERA) + { + double low = 0.5; + + if (cameraType == FIXED_CAMERA) + { + G3D::Vector3 scaler = extents.center() - cameraFocus.translation; + + cameraFocus.translation += scaler; + cameraGoal.translation += scaler; + } + + if (zoomType == ZOOM_OUT_ONLY || zoomType == ZOOM_CHAR_PART_DRAG) + low = (cameraGoal.translation - cameraFocus.translation).magnitude(); + + double current = (cameraGoal.translation - cameraFocus.translation).magnitude(); + + RBXASSERT(G3D::isFinite(current)); + + if (zoomType == ZOOM_CHAR_PART_DRAG) + extents.scale(1.1f); + + if (G3D::isFinite(low) && G3D::isFinite(current) && G3D::isFinite(1000.0)) + tryZoomExtents(low, current, 1000.0, extents, viewPort); + + cameraGoal = gCamera.getCoordinateFrame(); + if (Math::legalCameraCoord(currentCoord)) + { + gCamera.setCoordinateFrame(currentCoord); + } + else + { + RBXASSERT(0); + } + + ICameraOwner* owner = getCameraOwner(); + if (owner) + owner->cameraMoved(); + } + } + + bool Camera::zoomExtents(const G3D::Rect2D& viewPort) + { + ICameraOwner* owner = getCameraOwner(); + if (owner) + { + zoomExtents(owner->computeCameraOwnerExtents(), viewPort, ZOOM_IN_OR_OUT); + return true; + } + return false; + } + + void Camera::setCameraCoordinateFrameNoLerp(const G3D::CoordinateFrame& value) + { + cameraGoal = value; + goalToCamera(); + } +} \ No newline at end of file