Merge branch 'master' into llwebprofile

This commit is contained in:
Aleric Inglewood
2012-11-28 18:50:31 +01:00
10 changed files with 216 additions and 147 deletions

View File

@@ -418,6 +418,7 @@ void cleanupCurl(void)
if (CurlMultiHandle::getTotalMultiHandles() != 0)
llwarns << "Not all CurlMultiHandle objects were destroyed!" << llendl;
AIStateMachine::flush();
clearCommandQueue();
Stats::print();
ssl_cleanup();
@@ -491,8 +492,6 @@ void Stats::print(void)
// Even more strict, BufferedCurlEasyRequest may not be created directly either, only as
// base class of ThreadSafeBufferedCurlEasyRequest.
llassert(BufferedCurlEasyRequest_count == ThreadSafeBufferedCurlEasyRequest_count);
// Each AICurlEasyRequestStateMachine is responsible for exactly one easy handle.
llassert(easy_handles >= AICurlEasyRequest_count);
// Each AICurlEasyRequestStateMachine has one AICurlEasyRequest member.
llassert(AICurlEasyRequest_count >= AICurlEasyRequestStateMachine_count);
// AIFIXME: is this really always the case? And why?
@@ -1148,12 +1147,16 @@ void CurlEasyRequest::set_timeout_opts(void)
setopt(CURLOPT_TIMEOUT, mTimeoutPolicy->getCurlTransaction());
}
void CurlEasyRequest::create_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj)
void CurlEasyRequest::create_timeout_object(void)
{
ThreadSafeBufferedCurlEasyRequest* lockobj = NULL;
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
lockobj = static_cast<BufferedCurlEasyRequest*>(this)->get_lockobj();
#endif
mTimeout = new curlthread::HTTPTimeout(mTimeoutPolicy, lockobj);
}
LLPointer<curlthread::HTTPTimeout>& CurlEasyRequest::get_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj)
LLPointer<curlthread::HTTPTimeout>& CurlEasyRequest::get_timeout_object(void)
{
if (mTimeoutIsOrphan)
{
@@ -1162,7 +1165,7 @@ LLPointer<curlthread::HTTPTimeout>& CurlEasyRequest::get_timeout_object(ThreadSa
}
else
{
create_timeout_object(lockobj);
create_timeout_object();
}
return mTimeout;
}

View File

@@ -56,12 +56,17 @@ PerHostRequestQueuePtr PerHostRequestQueue::instance(std::string const& hostname
//static
void PerHostRequestQueue::release(PerHostRequestQueuePtr& instance)
{
if (instance->lastone())
if (instance->exactly_two_left()) // Being 'instance' and the one in sInstanceMap.
{
// The viewer can be have left main() we can't access the global sInstanceMap anymore.
if (LLApp::isStopped())
{
return;
}
instance_map_wat instance_map_w(sInstanceMap);
// It is possible that 'lastone' is not up to date anymore.
// It is possible that 'exactly_two_left' is not up to date anymore.
// Therefore, recheck the condition now that we have locked sInstanceMap.
if (!instance->lastone())
if (!instance->exactly_two_left())
{
// Some other thread added this host in the meantime.
return;

View File

@@ -118,7 +118,7 @@ class PerHostRequestQueue {
class RefCountedThreadSafePerHostRequestQueue : public threadsafe_PerHostRequestQueue {
public:
RefCountedThreadSafePerHostRequestQueue(void) : mReferenceCount(0) { }
bool lastone(void) const { llassert(mReferenceCount >= 2); return mReferenceCount == 2; }
bool exactly_two_left(void) const { return mReferenceCount == 2; }
private:
// Used by PerHostRequestQueuePtr. Object is deleted when reference count reaches zero.

View File

@@ -119,6 +119,7 @@ inline CURLMcode check_multi_code(CURLMcode code) { AICurlInterface::Stats::mult
bool curlThreadIsRunning(void);
void wakeUpCurlThread(void);
void stopCurlThread(void);
void clearCommandQueue(void);
#define DECLARE_SETOPT(param_type) \
CURLcode setopt(CURLoption option, param_type parameter)
@@ -316,7 +317,7 @@ class CurlEasyRequest : public CurlEasyHandle {
static CURLcode curlCtxCallback(CURL* curl, void* sslctx, void* parm);
// Called from get_timeout_object and httptimeout.
void create_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj);
void create_timeout_object(void);
public:
// Set default options that we want applied to all curl easy handles.
@@ -374,9 +375,9 @@ class CurlEasyRequest : public CurlEasyHandle {
std::string const& getLowercaseHostname(void) const { return mLowercaseHostname; }
// Called by CurlSocketInfo to allow access to the last (after a redirect) HTTPTimeout object related to this request.
// This creates mTimeout (unless mTimeoutIsOrphan is set in which case it adopts the orphan).
LLPointer<curlthread::HTTPTimeout>& get_timeout_object(ThreadSafeBufferedCurlEasyRequest* lockobj);
LLPointer<curlthread::HTTPTimeout>& get_timeout_object(void);
// Accessor for mTimeout with optional creation of orphaned object (if lockobj != NULL).
LLPointer<curlthread::HTTPTimeout>& httptimeout(ThreadSafeBufferedCurlEasyRequest* lockobj = NULL) { if (lockobj && !mTimeout) create_timeout_object(lockobj); return mTimeout; }
LLPointer<curlthread::HTTPTimeout>& httptimeout(void) { if (!mTimeout) { create_timeout_object(); mTimeoutIsOrphan = true; } return mTimeout; }
// Return true if no data has been received on the latest socket (if any) for too long.
bool has_stalled(void) const { return mTimeout && mTimeout->has_stalled(); }

View File

@@ -775,7 +775,7 @@ CurlSocketInfo::CurlSocketInfo(MultiHandle& multi_handle, CURL* easy, curl_socke
// CurlSocketInfo objects for a request and we need upload_finished() to be called on the HTTPTimeout
// object related to THIS CurlSocketInfo.
AICurlEasyRequest_wat easy_request_w(*lockobj);
mTimeout = easy_request_w->get_timeout_object(lockobj);
mTimeout = easy_request_w->get_timeout_object();
}
CurlSocketInfo::~CurlSocketInfo()
@@ -1065,6 +1065,10 @@ void AICurlThread::wakeup_thread(void)
DoutEntering(dc::curl, "AICurlThread::wakeup_thread");
llassert(is_main_thread());
// If we are already exiting the viewer then return immediately.
if (!mRunning)
return;
// Try if curl thread is still awake and if so, pass the new commands directly.
if (mWakeUpMutex.tryLock())
{
@@ -1609,7 +1613,9 @@ CURLMcode MultiHandle::remove_easy_request(addedEasyRequests_type::iterator cons
ThreadSafeBufferedCurlEasyRequest* lockobj = iter->get_ptr().get();
#endif
mAddedEasyRequests.erase(iter);
#if CWDEBUG
Dout(dc::curl, "MultiHandle::remove_easy_request: Removed AICurlEasyRequest " << (void*)lockobj << "; now processing " << mAddedEasyRequests.size() << " easy handles.");
#endif
// Attempt to add a queued request, if any.
PerHostRequestQueue_wat(*per_host)->add_queued_to(this);
@@ -1971,6 +1977,10 @@ void HTTPTimeout::done(AICurlEasyRequest_wat const& curlEasyRequest_w, CURLcode
DoutCurl("done: mStalled set to -1");
}
// Libcurl uses GetTickCount on windows, with a resolution of 10 to 16 ms.
// As a result, we can not assume that namelookup_time == 0 has a special meaning.
#define LOWRESTIMER LL_WINDOWS
void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request, char const* eff_url)
{
llwarns << "Request to \"" << curl_easy_request->getLowercaseHostname() << "\" timed out for " << curl_easy_request->getTimeoutPolicy()->name() << llendl;
@@ -1981,8 +1991,15 @@ void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request, ch
curl_easy_request->getinfo(CURLINFO_APPCONNECT_TIME, &appconnect_time);
curl_easy_request->getinfo(CURLINFO_PRETRANSFER_TIME, &pretransfer_time);
curl_easy_request->getinfo(CURLINFO_STARTTRANSFER_TIME, &starttransfer_time);
if (namelookup_time == 0)
if (namelookup_time == 0
#if LOWRESTIMER
&& connect_time == 0
#endif
)
{
#if LOWRESTIMER
llinfos << "Hostname seems to have been still in the DNS cache." << llendl;
#else
llwarns << "Huh? Curl returned CURLE_OPERATION_TIMEDOUT, but DNS lookup did not occur according to timings. Expected CURLE_COULDNT_RESOLVE_PROXY or CURLE_COULDNT_RESOLVE_HOST!" << llendl;
llassert(connect_time == 0);
llassert(appconnect_time == 0);
@@ -1990,17 +2007,26 @@ void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request, ch
llassert(starttransfer_time == 0);
// Fatal error for diagnostics.
return;
#endif
}
// If namelookup_time is less than 500 microseconds, then it's very likely just a DNS cache lookup.
else if (namelookup_time < 500e-6)
{
#if LOWRESTIMER
llinfos << "Hostname was most likely still in DNS cache (or lookup occured in under ~10ms)." << llendl;
#else
llinfos << "Hostname was still in DNS cache." << llendl;
#endif
}
else
{
llwarns << "DNS lookup of " << curl_easy_request->getLowercaseHostname() << " took " << namelookup_time << " seconds." << llendl;
llinfos << "DNS lookup of " << curl_easy_request->getLowercaseHostname() << " took " << namelookup_time << " seconds." << llendl;
}
if (connect_time == 0)
if (connect_time == 0
#if LOWRESTIMER
&& namelookup_time > 0 // connect_time, when set, is namelookup_time + something.
#endif
)
{
llwarns << "Huh? Curl returned CURLE_OPERATION_TIMEDOUT, but connection did not occur according to timings. Expected CURLE_COULDNT_CONNECT!" << llendl;
llassert(appconnect_time == 0);
@@ -2010,16 +2036,21 @@ void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request, ch
return;
}
// If connect_time is almost equal to namelookup_time, then it was just set because it was already connected.
if (connect_time - namelookup_time <= 1e-6)
if (connect_time - namelookup_time <= 1e-5)
{
#if LOWRESTIMER // Assuming 10ms resolution.
llinfos << "The socket was most likely already connected (or you connected to a proxy with a connect time of under ~10 ms)." << llendl;
#else
llinfos << "The socket was already connected (to remote or proxy)." << llendl;
#endif
// I'm assuming that the SSL/TLS handshake can be measured with a low res timer.
if (appconnect_time == 0)
{
llwarns << "The SSL/TLS handshake never occurred according to the timings!" << llendl;
return;
}
// If appconnect_time is almost equal to connect_time, then it was just set because this is a connection re-use.
if (appconnect_time - connect_time <= 1e-6)
if (appconnect_time - connect_time <= 1e-5)
{
llinfos << "Connection with HTTP server was already established; this was a re-used connection." << llendl;
}
@@ -2043,7 +2074,7 @@ void HTTPTimeout::print_diagnostics(CurlEasyRequest const* curl_easy_request, ch
llwarns << "The transfer never happened because there was too much in the pipeline (apparently)." << llendl;
return;
}
else if (pretransfer_time - appconnect_time >= 1e-6)
else if (pretransfer_time - appconnect_time >= 1e-5)
{
llinfos << "Apparently there was a delay, due to waits in line for the pipeline, of " << (pretransfer_time - appconnect_time) << " seconds before the transfer began." << llendl;
}
@@ -2123,10 +2154,14 @@ void stopCurlThread(void)
ms_sleep(10);
}
Dout(dc::curl, "Curl thread" << (curlThreadIsRunning() ? " not" : "") << " stopped after " << ((100 - count) * 10) << "ms.");
// Clear the command queue, for a cleaner cleanup.
}
}
void clearCommandQueue(void)
{
// Clear the command queue now in order to avoid the global deinitialization order fiasco.
command_queue_wat command_queue_w(command_queue);
command_queue_w->clear();
}
}
//-----------------------------------------------------------------------------
@@ -2240,11 +2275,8 @@ size_t BufferedCurlEasyRequest::curlReadCallback(char* data, size_t size, size_t
S32 bytes = size * nmemb; // The maximum amount to read.
self_w->mLastRead = self_w->getInput()->readAfter(sChannels.out(), self_w->mLastRead, (U8*)data, bytes);
self_w->mRequestTransferedBytes += bytes; // Accumulate data sent to the server.
// Timeout administration. Note that it can happen that we get here
// before the socket callback has been called, because the silly libcurl
// writes headers without informing us. In that case it's OK to create
// the Timeout object on the fly, so pass lockobj.
if (self_w->httptimeout(lockobj)->data_sent(bytes))
// Timeout administration.
if (self_w->httptimeout()->data_sent(bytes))
{
// Transfer timed out. Return CURL_READFUNC_ABORT which will abort with error CURLE_ABORTED_BY_CALLBACK.
return CURL_READFUNC_ABORT;
@@ -2337,10 +2369,10 @@ size_t BufferedCurlEasyRequest::curlHeaderCallback(char* data, size_t size, size
#if defined(CWDEBUG) || defined(DEBUG_CURLIO)
int debug_callback(CURL*, curl_infotype infotype, char* buf, size_t size, void* user_ptr)
{
BufferedCurlEasyRequest* request = (BufferedCurlEasyRequest*)user_ptr;
#ifdef CWDEBUG
using namespace ::libcwd;
BufferedCurlEasyRequest* request = (BufferedCurlEasyRequest*)user_ptr;
std::ostringstream marker;
marker << (void*)request->get_lockobj();
libcw_do.push_marker();

View File

@@ -624,7 +624,7 @@ void debug_curl_easy_reset(CURL* handle)
CURLcode debug_curl_easy_setopt(CURL* handle, CURLoption option, ...)
{
CURLcode ret;
CURLcode ret = CURLE_UNKNOWN_OPTION; // Suppress compiler warning.
va_list ap;
union param_type {
long along;
@@ -841,7 +841,7 @@ CURLMcode debug_curl_multi_remove_handle(CURLM* multi_handle, CURL* easy_handle)
CURLMcode debug_curl_multi_setopt(CURLM* multi_handle, CURLMoption option, ...)
{
CURLMcode ret;
CURLMcode ret = CURLM_UNKNOWN_OPTION; // Suppress compiler warning.
va_list ap;
union param_type {
long along;

View File

@@ -71,7 +71,7 @@ void LLStyle::drawControl(ControlElement element, const QStyleOption *option, QP
QPlastiqueStyle::drawControl(element, &localOption, painter, widget);
return;
}
default:
break;
}

View File

@@ -1381,74 +1381,28 @@ LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y )
BOOL hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
projected_mouse -= snap_plane_center;
S32 snap_plane = 0;
F32 dot = cam_to_snap_plane * constraint_axis;
if (llabs(dot) < 0.01f)
{
// looking at ring edge on, project onto view plane and check if mouse is past ring
getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
projected_mouse -= snap_plane_center;
dot = projected_mouse * constraint_axis;
if (projected_mouse * constraint_axis > 0)
{
snap_plane = 1;
}
projected_mouse -= dot * constraint_axis;
}
else if (dot > 0.f)
{
// look for mouse position outside and in front of snap circle
if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
{
snap_plane = 1;
}
}
else
{
// look for mouse position inside or in back of snap circle
if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
{
snap_plane = 1;
}
}
if (snap_plane == 0)
{
// try other plane
snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f));
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
}
else
{
cam_to_snap_plane = snap_plane_center - gAgentCamera.getCameraPositionAgent();
cam_to_snap_plane.normVec();
}
hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
projected_mouse -= snap_plane_center;
dot = cam_to_snap_plane * constraint_axis;
if (gSavedSettings.getBOOL("SnapEnabled")) {
S32 snap_plane = 0;
F32 dot = cam_to_snap_plane * constraint_axis;
if (llabs(dot) < 0.01f)
{
// looking at ring edge on, project onto view plane and check if mouse is past ring
getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
projected_mouse -= snap_plane_center;
dot = projected_mouse * constraint_axis;
if (projected_mouse * constraint_axis < 0)
if (projected_mouse * constraint_axis > 0)
{
snap_plane = 2;
snap_plane = 1;
}
projected_mouse -= dot * constraint_axis;
}
else if (dot < 0.f)
else if (dot > 0.f)
{
// look for mouse position outside and in front of snap circle
if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
{
snap_plane = 2;
snap_plane = 1;
}
}
else
@@ -1456,78 +1410,136 @@ LLQuaternion LLManipRotate::dragConstrained( S32 x, S32 y )
// look for mouse position inside or in back of snap circle
if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
{
snap_plane = 2;
snap_plane = 1;
}
}
}
if (snap_plane > 0)
{
LLVector3 cam_at_axis;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
if (snap_plane == 0)
{
cam_at_axis.setVec(1.f, 0.f, 0.f);
// try other plane
snap_plane_center = (center - (constraint_axis * mRadiusMeters * 0.5f));
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
cam_to_snap_plane.setVec(1.f, 0.f, 0.f);
}
else
{
cam_to_snap_plane = snap_plane_center - gAgentCamera.getCameraPositionAgent();
cam_to_snap_plane.normVec();
}
hit = getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, constraint_axis);
projected_mouse -= snap_plane_center;
dot = cam_to_snap_plane * constraint_axis;
if (llabs(dot) < 0.01f)
{
// looking at ring edge on, project onto view plane and check if mouse is past ring
getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_to_snap_plane);
projected_mouse -= snap_plane_center;
dot = projected_mouse * constraint_axis;
if (projected_mouse * constraint_axis < 0)
{
snap_plane = 2;
}
projected_mouse -= dot * constraint_axis;
}
else if (dot < 0.f)
{
// look for mouse position outside and in front of snap circle
if (hit && projected_mouse.magVec() > SNAP_GUIDE_INNER_RADIUS * mRadiusMeters && projected_mouse * cam_to_snap_plane < 0.f)
{
snap_plane = 2;
}
}
else
{
// look for mouse position inside or in back of snap circle
if (projected_mouse.magVec() < SNAP_GUIDE_INNER_RADIUS * mRadiusMeters || projected_mouse * cam_to_snap_plane > 0.f || !hit)
{
snap_plane = 2;
}
}
}
else
{
cam_at_axis = snap_plane_center - gAgentCamera.getCameraPositionAgent();
cam_at_axis.normVec();
}
// first, project mouse onto screen plane at point tangent to rotation radius.
getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis);
// project that point onto rotation plane
projected_mouse -= snap_plane_center;
projected_mouse -= projected_vec(projected_mouse, constraint_axis);
F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec());
F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
if (llabs(mouse_lateral_dist) > 0.01f)
{
mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
(mouse_lateral_dist * mouse_lateral_dist));
}
LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis);
projected_mouse -= mouse_depth * projected_camera_at;
if (!mInSnapRegime)
{
mSmoothRotate = TRUE;
}
mInSnapRegime = TRUE;
// 0 to 360 deg
F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
//fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
LLVector3 object_axis;
getObjectAxisClosestToMouse(object_axis);
object_axis = object_axis * first_object_node->mSavedRotation;
// project onto constraint plane
object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
object_axis.normVec();
if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
if (snap_plane > 0)
{
F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
LLVector3 cam_at_axis;
if (mObjectSelection->getSelectType() == SELECT_TYPE_HUD)
{
cam_at_axis.setVec(1.f, 0.f, 0.f);
}
else
{
cam_at_axis = snap_plane_center - gAgentCamera.getCameraPositionAgent();
cam_at_axis.normVec();
}
// first, project mouse onto screen plane at point tangent to rotation radius.
getMousePointOnPlaneAgent(projected_mouse, x, y, snap_plane_center, cam_at_axis);
// project that point onto rotation plane
projected_mouse -= snap_plane_center;
projected_mouse -= projected_vec(projected_mouse, constraint_axis);
F32 mouse_lateral_dist = llmin(SNAP_GUIDE_INNER_RADIUS * mRadiusMeters, projected_mouse.magVec());
F32 mouse_depth = SNAP_GUIDE_INNER_RADIUS * mRadiusMeters;
if (llabs(mouse_lateral_dist) > 0.01f)
{
mouse_depth = sqrtf((SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) * (SNAP_GUIDE_INNER_RADIUS * mRadiusMeters) -
(mouse_lateral_dist * mouse_lateral_dist));
}
LLVector3 projected_camera_at = cam_at_axis - projected_vec(cam_at_axis, constraint_axis);
projected_mouse -= mouse_depth * projected_camera_at;
if (!mInSnapRegime)
{
mSmoothRotate = TRUE;
}
mInSnapRegime = TRUE;
// 0 to 360 deg
F32 mouse_angle = fmodf(atan2(projected_mouse * axis1, projected_mouse * axis2) * RAD_TO_DEG + 360.f, 360.f);
F32 relative_mouse_angle = fmodf(mouse_angle + (SNAP_ANGLE_DETENTE / 2), SNAP_ANGLE_INCREMENT);
//fmodf(llround(mouse_angle * RAD_TO_DEG, 7.5f) + 360.f, 360.f);
LLVector3 object_axis;
getObjectAxisClosestToMouse(object_axis);
object_axis = object_axis * first_object_node->mSavedRotation;
// project onto constraint plane
object_axis = object_axis - (object_axis * getConstraintAxis()) * getConstraintAxis();
object_axis.normVec();
if (relative_mouse_angle < SNAP_ANGLE_DETENTE)
{
F32 quantized_mouse_angle = mouse_angle - (relative_mouse_angle - (SNAP_ANGLE_DETENTE * 0.5f));
angle = (quantized_mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
}
else
{
angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
}
return LLQuaternion( -angle, constraint_axis );
}
else
{
angle = (mouse_angle * DEG_TO_RAD) - atan2(object_axis * axis1, object_axis * axis2);
if (mInSnapRegime)
{
mSmoothRotate = TRUE;
}
mInSnapRegime = FALSE;
}
return LLQuaternion( -angle, constraint_axis );
}
else
{
else {
if (mInSnapRegime)
{
mSmoothRotate = TRUE;
}
mInSnapRegime = FALSE;
}
if (!mInSnapRegime)
{
LLVector3 up_from_axis = mCenterToCamNorm % constraint_axis;
up_from_axis.normVec();
LLVector3 cur_intersection;