/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */ #pragma once #include "V8DataModel/PVInstance.h" #include "V8DataModel/Surface.h" #include "util/PartMaterial.h" #include "V8World/Primitive.h" #include "V8World/IMoving.h" #include "V8World/MaterialProperties.h" #include "V8DataModel/Camera.h" #include "GfxBase/Part.h" #include "GfxBase/IAdornable.h" #include "Util/CameraSubject.h" #include "Util/Selectable.h" #include "Util/BrickColor.h" #include "Util/SystemAddress.h" #include "Util/Faces.h" #include "Util/PathInterpolatedCFrame.h" #include "Util/CompactEnum.h" #include "rbx/rbxTime.h" #include "RBX/Intrusive/Set.h" #include "Reflection/Property.h" #include "Util/Average.h" #include #include LOGGROUP(TouchedSignal) namespace RBX { class PartAttribute; class World; class Primitive; class Assembly; class GfxPart; class Clump; class TouchTransmitter; class SurfaceGui; class Humanoid; class Workspace; struct RootPrimitiveOwnershipData; typedef RBX::Intrusive::Set< class PartInstance, class PhysicsService >::Hook PhysicsServiceHook; extern const char* const sPart; class PartInstance : public DescribedNonCreatable , public IMoving , public IAdornable , public CameraSubject , public Diagnostics::Countable , public PhysicsServiceHook { private: typedef DescribedNonCreatable Super; friend class Surface; friend class Surfaces; public: static bool showContactPoints; static bool showJointCoordinates; static bool highlightSleepParts; static bool highlightAwakeParts; static bool showBodyTypes; static bool showAnchoredParts; static bool showPartCoordinateFrames; static bool showUnalignedParts; static bool showSpanningTree; static bool showMechanisms; static bool showAssemblies; static bool showReceiveAge; static bool disableInterpolation; static bool showInterpolationPath; float getReceiveInterval() const; enum FormFactor { SYMETRIC = 0, BRICK, PLATE, CUSTOM }; enum LegacyPartType { BALL_LEGACY_PART = 0, BLOCK_LEGACY_PART, CYLINDER_LEGACY_PART, SPECIAL_LEGACY_PART }; static float plateHeight() {return 0.4f;} static float brickHeight() {return 1.2f;} static float cameraTransparentDistance() {return 3.0f;} static float cameraTranslucentDistance() {return 6.0f;} void resetNetworkOwnerTime(double extraTime); bool networkOwnerTimeUp() const; virtual shared_ptr getTouchingParts(); // util for below functions static bool partIsLegacyCustomPhysProperties(const PartInstance* part); // conversion functions for insert and dataModel load static void convertToNewPhysicalPropRecursive(RBX::Instance* instance); static bool instanceOrChildrenContainsNewCustomPhysics(RBX::Instance* instance); // Used to - judiciously - extend data into PartInstance boost::uint64_t raknetTime; private: Vector3 xmlToUiSize(const Vector3& xmlSize) const; boost::scoped_ptr primitive; // engine object GfxPart* gfxPart; // simple link to graphics object. unsigned int cookie; boost::scoped_ptr renderingCoordinateFrame; Time networkOwnerTime; // prevent too much chatter // streamable data stored in Primitive // friction, elasticity, partSize, position, velocity // Streamable data protected: BrickColor color; float transparency; float reflectance; float localTransparencyModifier; bool cachedNetworkOwnerIsSomeoneElse; bool locked; CompactEnum formFactor; CompactEnum legacyPartType; private: bool computeSurfacesNeedAdorn() const; protected: void safeMove(); // call PVInstance::safeMove public: rbx::remote_signal* getOrCreateNetworkOwnerChangedSignal(bool create); //helper function static void printNetAPIDisabledMessage(); // Networking - streaming, no UI static const Reflection::PropDescriptor prop_NetworkOwner; static const Reflection::PropDescriptor prop_NetworkIsSleeping; static const Reflection::PropDescriptor prop_ConfirmedNetworkOwner; static const Reflection::PropDescriptor prop_TransitionalNetworkOwner; // debug - HIDDEN_SCRIPTING static const Reflection::PropDescriptor prop_ReceiveAge; // static const Reflection::PropDescriptor prop_Color; static const Reflection::PropDescriptor prop_BrickColor; static const Reflection::PropDescriptor prop_Transparency; static const Reflection::PropDescriptor prop_LocalTransparencyModifier; static const Reflection::EnumPropDescriptor prop_renderMaterial; static const Reflection::PropDescriptor prop_Reflectance; static const Reflection::PropDescriptor prop_Locked; static const Reflection::PropDescriptor prop_CanCollide; static const Reflection::PropDescriptor prop_Anchored; static const Reflection::PropDescriptor prop_Size; static const Reflection::PropDescriptor prop_CFrame; static const Reflection::PropDescriptor prop_Elasticity; static const Reflection::PropDescriptor prop_Friction; static const Reflection::PropDescriptor prop_CustomPhysicalProperties; static const Reflection::PropDescriptor prop_Velocity; static const Reflection::PropDescriptor prop_RotVelocity; class TouchedSignal : public rbx::signal)> { private: typedef rbx::signal)> Super; class TouchedSlot { boost::function)> inner; boost::weak_ptr const source; public: TouchedSlot(const boost::function)>& inner, PartInstance* source):inner(inner),source(shared_from(source)) { source->incrementTouchedSlotCount(); } ~TouchedSlot() { shared_ptr source = this->source.lock(); if (source) source->decrementTouchedSlotCount(); } void operator()(shared_ptr instance) { inner(instance); } TouchedSlot(const TouchedSlot& other) : inner(other.inner), source(other.source) { shared_ptr source = this->source.lock(); if (source) source->incrementTouchedSlotCount(); } TouchedSlot& operator=(const TouchedSlot& other); }; public: PartInstance* part; template rbx::signals::connection connect(F slot) { FASTLOG1(FLog::TouchedSignal, "Signal connected (no upper message = lua) - %p", this); rbx::signals::connection con = Super::connect(TouchedSlot(slot, part)); if(FLog::TouchedSignal) { FASTLOG1(FLog::TouchedSignal, "Signal connected - %p", this); con.flogPrint(); Super::flogPrint(); } return con; } void operator()(shared_ptr instance) { if(FLog::TouchedSignal) Super::flogPrint(); Super::operator()(instance); } }; class OnDemandPartInstance : public OnDemandInstance, public Allocator { public: OnDemandPartInstance(PartInstance* owner); void *operator new(size_t sz) { return Allocator::operator new(sz); } void operator delete(void*p) { return Allocator::operator delete(p); } rbx::signal sleepingChangedSignal; rbx::signal)> clumpChangedSignal; // Fired for humanoid state when CFrame changed from reflection rbx::signal cframeChangedFromReflectionSignal; TouchTransmitter* touchTransmitter; rbx::atomic touchedSlotCount; TouchedSignal touchedSignal; TouchedSignal touchEndedSignal; rbx::signal)> deprecatedStoppedTouchingSignal; rbx::signal outfitChangedSignal; rbx::signal)> localSimulationTouchedSignal; rbx::signal buoyancyChangedSignal; rbx::signal onPositionUpdatedByNetworkSignal; bool isCurrentlyStreamRemovingPart; std::vector< weak_ptr > surfaceGuiCookies; }; void reportTouch(const shared_ptr& other); void reportUntouch(const shared_ptr& other); TouchedSignal* getOrCreateTouchedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->touchedSignal : NULL; } TouchedSignal* getOrCreateTouchedEndedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->touchEndedSignal : NULL; } rbx::signal)>* getOrCreateDeprecatedStoppedTouchingSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->deprecatedStoppedTouchingSignal : NULL; } rbx::signal)>* getOrCreateLocalSimulationTouchedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->localSimulationTouchedSignal : NULL; } rbx::signal* getOrCreateOutfitChangedSignal(bool create = true) { return (onDemandRead() || create) ? &onDemandWrite()->outfitChangedSignal : NULL; } static bool nonNullInWorkspace(const shared_ptr& part); //PartInstance(); PartInstance(const Vector3& initialSize = Vector3(4.0f, 1.2f, 2.0f)); virtual ~PartInstance(); virtual bool hasThreeDimensionalSize() {return true;} virtual PartType getPartType() const {return BLOCK_PART; } virtual const Vector3& getMinimumUiSize() const {static Vector3 minSize(1.0f,1.0f,1.0f); return minSize;} virtual const Vector3& getMinimumUiSizeCustom() const {static Vector3 minSize(0.2f,0.2f,0.2f); return minSize;} virtual Faces getResizeHandleMask() const {return Faces(NORM_ALL_MASK); } Primitive* getPartPrimitive() {return primitive.get();} const Primitive* getConstPartPrimitive() const {return primitive.get();} virtual const Primitive* getConstPartPrimitiveVirtual() const {return primitive.get();} virtual bool getDragUtilitiesSupport() const { return true; } GfxPart* getGfxPart() const { return gfxPart; }; void setGfxPart(GfxPart* p) { gfxPart = p; }; unsigned int getCookie() const { return cookie; }; void setCookie(unsigned int cookie) { this->cookie = cookie; }; virtual void humanoidChanged(); Humanoid* findAncestorModelWithHumanoid(); const std::vector< weak_ptr >* getSurfaceGuiCookiesRead() const { return onDemandRead()? &onDemandRead()->surfaceGuiCookies : 0; } std::vector< weak_ptr >* getSurfaceGuiCookiesWrite() { return &onDemandWrite()->surfaceGuiCookies; } static Primitive* getPrimitive(PartInstance* p) {return p ? p->getPartPrimitive() : NULL;} static const Primitive* getConstPrimitive(const PartInstance* p) {return p ? p->getConstPartPrimitive() : NULL;} static PartInstance* fromPrimitive(Primitive* p); static const PartInstance* fromConstPrimitive(const Primitive* p); Clump* getClump(); const Clump* getConstClump() const; static PartInstance* fromAssembly(Assembly* a); static const PartInstance* fromConstAssembly(const Assembly* a); static void primitivesToParts(const G3D::Array& primitives, std::vector >& parts); static void findParts( Instance* instance, std::vector >& parts); /////////////////////////////////////////////////////////////////////////////////// // STREAMABLE access functions for PartInstance // // Data stored in the Primitive // private: bool resizeImpl(NormalId localNormalId, int amount); bool advResizeImpl(NormalId localNormalId, float amount, bool checkIntersection = true); void getConnectedPartsRecursiveImpl(shared_ptr &objs, boost::unordered_set &seen) const; void gatherResetOwnershipDataPreAnchor(std::vector& futureMechRoots, RootPrimitiveOwnershipData& ownershipData); void updateOwnershipSettingsPostAnchor(std::vector& futureMechRoots, RootPrimitiveOwnershipData& ownershipData); void gatherResetOwnershipDataPreUnanchor(std::vector& previousOwnershipRoots); void updateOwnershipSettingsPostUnAnchor(std::vector& previousOwnershipRoots); void updateResetOwnershipSettingsOnUnAnchor(); public: //private: THESE WANT TO BE PRIVATE, BUT CAN"T BE virtual void setPartSizeUi(const Vector3& uiSize); void setPartSizeUnjoined(const Vector3& uiSize); Vector3 getPartSizeUi() const; virtual void setTranslationUi(const Vector3& set); virtual const Vector3& getTranslationUi() const; virtual void setRotationUi(const Vector3& set); virtual const Vector3 getRotationUi() const; SurfaceType getSurfaceType(NormalId surfId) const; void setSurfaceType(NormalId surfId, SurfaceType type); LegacyController::InputType getInput(NormalId surfId) const; void setSurfaceInput(NormalId surfId, LegacyController::InputType inputType); float getParamA(NormalId surfId) const; void setParamA(NormalId surfId, float paramA); float getParamB(NormalId surfId) const; void setParamB(NormalId surfId, float paramB); bool isStandardPart() const; protected: void refreshPartSizeUi(); virtual Vector3 uiToXmlSize(const Vector3& uiSize) const; bool hasLegacyOffset(); virtual OnDemandInstance* initOnDemand(); public: const OnDemandPartInstance* onDemandRead() const { return static_cast(Super::onDemandRead()); }; OnDemandPartInstance* onDemandWrite() { return static_cast(Super::onDemandWrite()); }; virtual void setPartSizeXml(const Vector3& rbxSize); const Vector3& getPartSizeXml() const; virtual int getResizeIncrement() const { return 1; } virtual float getMinimumResizeIncrement() const { return 0.01f; } virtual float getMinimumYDimension() const; virtual float getMinimumXOrZDimension() const; virtual bool resize(NormalId localNormalId, int amount); bool resizeFloat(NormalId localNormalId, float amount, bool checkIntersection = true); virtual FormFactor getFormFactor() const { return SYMETRIC; } // these function should be called from network // "timeStamp" parameter used for the actual time the change happened, used for interpolation void addInterpolationSample(const CoordinateFrame& cf, const Velocity& vel, const RemoteTime& timeStamp, Time now, float localTimeOffset = 0.f, int numNodesAhead = 0); void setInterpolationDelay(float value); // these function should be called from setCoordinateFrame() void setPhysics(const PV& pv); void setPhysics(const CoordinateFrame& cf); virtual void setCoordinateFrame(const CoordinateFrame& value); void setCoordinateFrameRoot(const CoordinateFrame& value); const CoordinateFrame& getCoordinateFrame() const; CoordinateFrame calcRenderingCoordinateFrame(); // calculates a new interpolated render position CoordinateFrame calcRenderingCoordinateFrame(const Time& t); float getMass() const; int getMassScript(lua_State* L); Vector3 getPrincipalMoment() const; const Velocity& getVelocity() const; virtual void setLinearVelocity(const Vector3& set); const Vector3& getLinearVelocity() const; virtual void setRotationalVelocity(const Vector3& set); void setRotationalVelocityRoot(const Vector3& set); const Vector3& getRotationalVelocity() const; virtual void setFriction(float friction); float getFriction() const; virtual void setElasticity(float elasticity); float getElasticity() const; virtual void setPhysicalProperties(const PhysicalProperties& prop); const PhysicalProperties& getPhysicalProperties() const; float getSpecificGravity() const; virtual Surface getSurface(const RbxRay& gridRay, int& surfaceId); bool getCanCollide() const; virtual void setCanCollide(bool value); bool getAnchored() const; virtual void setAnchored(bool value); bool getDragging() const; void setDragging(bool value); bool isGrounded(); shared_ptr getConnectedParts(bool recursive); shared_ptr getRootPart(); ///////////////////////////////////////////////////////////////////// // virtual void join(); // functions - try to join or unjoin a part void destroyImplicitJoints(); virtual void destroyJoints(); /////////////////////////////////////////////////////////////////////// // Data stored in the PartInstance // const RBX::SystemAddress getNetworkOwner() const; void setNetworkOwner(const RBX::SystemAddress value); void setNetworkOwnerNotifyIfServer(const RBX::SystemAddress value, bool ownershipBecomingManual); void setNetworkOwnershipRuleIfServer(NetworkOwnership value); shared_ptr getNetworkOwnerScript(); void setNetworkOwnerScript(const shared_ptr playerInstance); bool getNetworkOwnershipAuto(); void setNetworkOwnershipAuto(); shared_ptr canSetNetworkOwnershipScript(); bool canSetNetworkOwnership(Primitive*& rootPrimitive, std::string& statusMessage); void setNetworkOwnerAndNotify(const RBX::SystemAddress value); void notifyNetworkOwnerChanged(const RBX::SystemAddress oldOwner); #ifdef RBX_TEST_BUILD rbx::signal ownershipChangeSignal; #endif bool getNetworkIsSleeping() const; void setNetworkIsSleeping(bool value); PartMaterial getRenderMaterial() const; virtual void setRenderMaterial(PartMaterial value); void setNetworkOwnershipManualReplicate(bool value); const bool getNetworkOwnershipManualReplicate() const; void setNetworkOwnershipRule(NetworkOwnership value); const NetworkOwnership getNetworkOwnershipRule() const; float getTransparencyXml() const {return transparency;} float getTransparencyUi() const {return (1.0f - ((1.0f - localTransparencyModifier) * (1.0f - getTransparencyXml()))); } bool getIsTransparent() const {return getTransparencyUi() > 0.1f;} virtual void setTransparency(float value); void setLocalTransparencyModifier(float value); float getLocalTransparencyModifier() const {return localTransparencyModifier;} float getReflectance() const {return reflectance;}// if(transparency != 0) return this->reflectance; else return 0; } virtual void setReflectance(float value); BrickColor getColor() const {return color;} virtual void setColor(BrickColor value); Color3 getColor3() const {return getColor().color3();} void setColor3(const Color3& value) { setColor(BrickColor::closest(value)); } bool getPartLocked() const {return locked;} virtual void setPartLocked(bool value); void incrementTouchedSlotCount(); void decrementTouchedSlotCount(); /////////////////////////////////////////////////////////////////////// // // Utilities // used for SetNetworkOwnership API static bool isPlayerCharacterPart(PartInstance* part); static void checkConsistentOwnerAndRuleResetRoots(RBX::SystemAddress& consistentAddress, bool& hasConsistentOwner, bool& hasConsistentManualOwnershipRule, std::vector& primRoots); // version of PartInstance used by Draw.cpp Part getPart(); static bool getLocked(Instance* instance); static void setLocked(Instance* instance, bool value); float alpha() const {return (1.0f - getTransparencyUi());} bool lockedInPlace() const; // true if you could not normally "pull" this part out bool aligned(bool useGlobalGridSetting = false) const; // true if aligned to the 0.1 grid and rotated to xyz CoordinateFrame worldSnapLocation() const; bool computeNetworkOwnerIsSomeoneElse() const; bool isProjectile() const; virtual void onChildRemoved(Instance* child); void updatePrimitiveState(); void setIsCurrentlyStreamRemovingPart(); //////////////////////////////////////////////////////////////////////// // IMoving // /*implement*/ void onSleepingChanged(bool sleeping); /*implement*/ bool reportTouches() const; /*implement*/ void onClumpChanged(); /*implement*/ void onNetworkIsSleepingChanged(Time now); // callback to PartInstance from Primitive /*implement*/ void onBuoyancyChanged( bool value ); // callback to PartInstance from Primitive /*implement*/ bool isInContinousMotion(); // callback to PartInstance from Primitive void fireOutfitChanged(); //////////////////////////////////////////////////////////////////////// // Instance // /*override*/ void onAncestorChanged(const AncestorChanged& event); /*override*/ void onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider); /*override*/ bool askSetParent(const Instance* instance) const; /*override*/ void onGuidChanged(); /*override*/ void setName(const std::string& value); /*override*/ int getPersistentDataCost() const { return Super::getPersistentDataCost() + 10; } /*override*/ void processRemoteEvent(const Reflection::EventDescriptor& descriptor, const Reflection::EventArguments& args, const SystemAddress& source); //////////////////////////////////////////////////////////////////////// // IHasLocation // /*override*/ const CoordinateFrame getLocation() {return getCoordinateFrame();} //////////////////////////////////////////////////////////////////////// // CameraSubject // /*override*/ const CoordinateFrame getRenderLocation() {return calcRenderingCoordinateFrame();} /*override*/ const Vector3 getRenderSize() { return getPartSizeUi(); } /*override*/ void onCameraNear(float distance); /*override*/ virtual void getCameraIgnorePrimitives(std::vector& primitives) {primitives.push_back(getPartPrimitive());} //////////////////////////////////////////////////////////////////////// // IAdornable3d // /*override*/ virtual bool shouldRender3dAdorn() const; /*override*/ virtual void render3dAdorn(Adorn* adorn); /*override*/ virtual void render3dSelect(Adorn* adorn, SelectState selectState); //////////////////////////////////////////////////////////////////////// // IHasPrimaryPart // /*implement*/ PartInstance* getPrimaryPart() {return this;} //////////////////////////////////////////////////////////////////////// // overrides from PVInstance // /*override*/ bool hitTestImpl(const RbxRay& worldRay, Vector3& worldHitPoint); /*override*/ Extents computeExtentsWorld() const; bool containedByFrustum(const RBX::Frustum& frustum) const; bool intersectFrustum(const RBX::Frustum& frustum) const; private: void removeTouchTransmitter(); void addTouchTransmitter(); bool hasTouchTransmitter() const { return onDemandRead() && onDemandRead()->touchTransmitter; } void updateHumanoidCookie(); void onPVChangedFromReflection(); void createRenderingCoordinateFrame(); CoordinateFrame calcRenderingCoordinateFrameImpl(const Time& t); CoordinateFrame computeRenderingCoordinateFrame(PartInstance* mechanismRootPart, const Time& t); bool getIsCurrentlyStreamRemovingPart() const; //////////////////////////////////////////////////////////////////////// // PartInstance // void onSurfaceChanged(NormalId surfId); // callback from Surface // TODO: Nuke this when we refactor Surface void raiseSurfacePropertyChanged(const Reflection::PropertyDescriptor& desc) { this->raisePropertyChanged(desc); } static float defaultFriction() {return 0.3f;} static float defaultElasticity() {return 0.5f;} }; } // namespace