const LLVector3 LLDrawable::getPositionAgent() const { if (getVOVolume()) { if (isActive()) { if (!isRoot()) { LLVector4a pos; pos.load3(mVObjp->getPosition().mV); getRenderMatrix().affineTransform(pos,pos); return LLVector3(pos.getF32ptr()); } else { return LLVector3(getRenderMatrix().getRow<3>().getF32ptr()); } } else { return mVObjp->getPositionAgent(); } } else { return getWorldPosition(); } }
void LLDrawable::shiftPos(const LLVector4a &shift_vector) { if (isDead()) { llwarns << "Shifting dead drawable" << llendl; return; } if (mParent) { mXform.setPosition(mVObjp->getPosition()); } else { mXform.setPosition(mVObjp->getPositionAgent()); } mXform.setRotation(mVObjp->getRotation()); mXform.setScale(1,1,1); mXform.updateMatrix(); if (isStatic()) { LLVOVolume* volume = getVOVolume(); if (!volume) { gPipeline.markRebuild(this, LLDrawable::REBUILD_ALL, TRUE); } for (S32 i = 0; i < getNumFaces(); i++) { LLFace *facep = getFace(i); facep->mCenterAgent += LLVector3(shift_vector.getF32ptr()); facep->mExtents[0].add(shift_vector); facep->mExtents[1].add(shift_vector); if (!volume && facep->hasGeometry()) { facep->clearVertexBuffer(); } } mExtents[0].add(shift_vector); mExtents[1].add(shift_vector); mPositionGroup.add(shift_vector); } else if (mSpatialBridge) { mSpatialBridge->shiftPos(shift_vector); } else if (isAvatar()) { mExtents[0].add(shift_vector); mExtents[1].add(shift_vector); mPositionGroup.add(shift_vector); } mVObjp->onShift(shift_vector); }
void LLVolumeImplFlexible::onShift(const LLVector4a &shift_vector) { //VECTORIZE THIS LLVector3 shift(shift_vector.getF32ptr()); for (int section = 0; section < (1<<FLEXIBLE_OBJECT_MAX_SECTIONS)+1; ++section) { mSection[section].mPosition += shift; } }
BOOL LLViewerCamera::areVertsVisible(LLViewerObject* volumep, BOOL all_verts) { S32 i, num_faces; LLDrawable* drawablep = volumep->mDrawable; if (!drawablep) { return FALSE; } LLVolume* volume = volumep->getVolume(); if (!volume) { return FALSE; } LLVOVolume* vo_volume = (LLVOVolume*) volumep; vo_volume->updateRelativeXform(); LLMatrix4 mat = vo_volume->getRelativeXform(); LLMatrix4 render_mat(vo_volume->getRenderRotation(), LLVector4(vo_volume->getRenderPosition())); LLMatrix4a render_mata; render_mata.loadu(render_mat); LLMatrix4a mata; mata.loadu(mat); num_faces = volume->getNumVolumeFaces(); for (i = 0; i < num_faces; i++) { const LLVolumeFace& face = volume->getVolumeFace(i); for (U32 v = 0; v < face.mNumVertices; v++) { const LLVector4a& src_vec = face.mPositions[v]; LLVector4a vec; mata.affineTransform(src_vec, vec); if (drawablep->isActive()) { LLVector4a t = vec; render_mata.affineTransform(t, vec); } BOOL in_frustum = pointInFrustum(LLVector3(vec.getF32ptr())) > 0; if (( !in_frustum && all_verts) || (in_frustum && !all_verts)) { return !all_verts; } } } return all_verts; }
LLCamera LLSpatialBridge::transformCamera(LLCamera& camera) { LLCamera ret = camera; LLXformMatrix* mat = mDrawable->getXform(); const LLVector4a& center = mat->getWorldMatrix().getRow<3>(); LLQuaternion2 invRot; invRot.setConjugate( LLQuaternion2(mat->getRotation()) ); LLVector4a delta; delta.load3(ret.getOrigin().mV); delta.sub(center); LLVector4a lookAt; lookAt.load3(ret.getAtAxis().mV); LLVector4a up_axis; up_axis.load3(ret.getUpAxis().mV); LLVector4a left_axis; left_axis.load3(ret.getLeftAxis().mV); delta.setRotated(invRot, delta); lookAt.setRotated(invRot, lookAt); up_axis.setRotated(invRot, up_axis); left_axis.setRotated(invRot, left_axis); if (!delta.isFinite3()) { delta.clear(); } ret.setOrigin(LLVector3(delta.getF32ptr())); ret.setAxes(LLVector3(lookAt.getF32ptr()), LLVector3(left_axis.getF32ptr()), LLVector3(up_axis.getF32ptr())); return ret; }
BOOL LLVOSurfacePatch::lineSegmentIntersect(const LLVector4a& start, const LLVector4a& end, S32 face, BOOL pick_transparent, S32 *face_hitp, LLVector4a* intersection,LLVector2* tex_coord, LLVector4a* normal, LLVector4a* tangent) { if (!lineSegmentBoundingBox(start, end)) { return FALSE; } LLVector4a da; da.setSub(end, start); LLVector3 delta(da.getF32ptr()); LLVector3 pdelta = delta; pdelta.mV[2] = 0; F32 plength = pdelta.length(); F32 tdelta = 1.f/plength; LLVector3 v_start(start.getF32ptr()); LLVector3 origin = v_start - mRegionp->getOriginAgent(); if (mRegionp->getLandHeightRegion(origin) > origin.mV[2]) { //origin is under ground, treat as no intersection return FALSE; } //step one meter at a time until intersection point found //VECTORIZE THIS const LLVector4a* exta = mDrawable->getSpatialExtents(); LLVector3 ext[2]; ext[0].set(exta[0].getF32ptr()); ext[1].set(exta[1].getF32ptr()); F32 rad = (delta*tdelta).magVecSquared(); F32 t = 0.f; while ( t <= 1.f) { LLVector3 sample = origin + delta*t; if (AABBSphereIntersectR2(ext[0], ext[1], sample+mRegionp->getOriginAgent(), rad)) { F32 height = mRegionp->getLandHeightRegion(sample); if (height > sample.mV[2]) { //ray went below ground, positive intersection //quick and dirty binary search to get impact point tdelta = -tdelta*0.5f; F32 err_dist = 0.001f; F32 dist = fabsf(sample.mV[2] - height); while (dist > err_dist && tdelta*tdelta > 0.0f) { t += tdelta; sample = origin+delta*t; height = mRegionp->getLandHeightRegion(sample); if ((tdelta < 0 && height < sample.mV[2]) || (height > sample.mV[2] && tdelta > 0)) { //jumped over intersection point, go back tdelta = -tdelta; } tdelta *= 0.5f; dist = fabsf(sample.mV[2] - height); } if (intersection) { F32 height = mRegionp->getLandHeightRegion(sample); if (fabsf(sample.mV[2]-height) < delta.length()*tdelta) { sample.mV[2] = mRegionp->getLandHeightRegion(sample); } intersection->load3((sample + mRegionp->getOriginAgent()).mV); } if (normal) { normal->load3((mRegionp->getLand().resolveNormalGlobal(mRegionp->getPosGlobalFromRegion(sample))).mV); } return TRUE; } } t += tdelta; if (t > 1 && t < 1.f+tdelta*0.99f) { //make sure end point is checked (saves vertical lines coming up negative) t = 1.f; } } return FALSE; }
void LLPanelPrimMediaControls::updateShape() { LLViewerMediaImpl* media_impl = getTargetMediaImpl(); LLViewerObject* objectp = getTargetObject(); if(!media_impl || gFloaterTools->getVisible()) { setVisible(FALSE); return; } LLPluginClassMedia* media_plugin = NULL; if(media_impl->hasMedia()) { media_plugin = media_impl->getMediaPlugin(); } LLParcel *parcel = LLViewerParcelMgr::getInstance()->getAgentParcel(); bool can_navigate = parcel->getMediaAllowNavigate(); bool enabled = false; bool is_zoomed = (mCurrentZoom != ZOOM_NONE) && (mTargetObjectID == mZoomObjectID) && (mTargetObjectFace == mZoomObjectFace); // There is no such thing as "has_focus" being different from normal controls set // anymore (as of user feedback from bri 10/09). So we cheat here and force 'has_focus' // to 'true' (or, actually, we use a setting) bool has_focus = (gSavedSettings.getBOOL("PrimMediaControlsUseHoverControlSet")) ? media_impl->hasFocus() : true; setVisible(enabled); if (objectp) { bool mini_controls = false; LLMediaEntry *media_data = objectp->getTE(mTargetObjectFace)->getMediaData(); if (media_data && NULL != dynamic_cast<LLVOVolume*>(objectp)) { // Don't show the media controls if we do not have permissions enabled = dynamic_cast<LLVOVolume*>(objectp)->hasMediaPermission(media_data, LLVOVolume::MEDIA_PERM_CONTROL); mini_controls = (LLMediaEntry::MINI == media_data->getControls()); } const bool is_hud = objectp->isHUDAttachment(); // // Set the state of the buttons // // XXX RSP: TODO: FIXME: clean this up so that it is clearer what mode we are in, // and that only the proper controls get made visible/enabled according to that mode. mBackCtrl->setVisible(has_focus); mFwdCtrl->setVisible(has_focus); mReloadCtrl->setVisible(has_focus); mStopCtrl->setVisible(false); mHomeCtrl->setVisible(has_focus); mZoomCtrl->setVisible(!is_zoomed); mUnzoomCtrl->setVisible(is_zoomed); mOpenCtrl->setVisible(true); mMediaAddressCtrl->setVisible(has_focus && !mini_controls); mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); mVolumeCtrl->setVisible(false); mWhitelistIcon->setVisible(!mini_controls && (media_data)?media_data->getWhiteListEnable():false); // Disable zoom if HUD mZoomCtrl->setEnabled(!is_hud); mUnzoomCtrl->setEnabled(!is_hud); mSecureLockIcon->setVisible(false); mCurrentURL = media_impl->getCurrentMediaURL(); mBackCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateBack() && can_navigate); mFwdCtrl->setEnabled((media_impl != NULL) && media_impl->canNavigateForward() && can_navigate); mStopCtrl->setEnabled(has_focus && can_navigate); mHomeCtrl->setEnabled(has_focus && can_navigate); LLPluginClassMediaOwner::EMediaStatus result = ((media_impl != NULL) && media_impl->hasMedia()) ? media_plugin->getStatus() : LLPluginClassMediaOwner::MEDIA_NONE; mVolumeCtrl->setVisible(has_focus); mVolumeCtrl->setEnabled(has_focus); mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); if(media_plugin && media_plugin->pluginSupportsMediaTime()) { mReloadCtrl->setEnabled(false); mReloadCtrl->setVisible(false); mMediaStopCtrl->setVisible(has_focus); mHomeCtrl->setVisible(has_focus); mBackCtrl->setVisible(false); mFwdCtrl->setVisible(false); mMediaAddressCtrl->setVisible(false); mMediaAddressCtrl->setEnabled(false); mMediaPlaySliderPanel->setVisible(has_focus && !mini_controls); mMediaPlaySliderPanel->setEnabled(has_focus && !mini_controls); mSkipFwdCtrl->setVisible(has_focus && !mini_controls); mSkipFwdCtrl->setEnabled(has_focus && !mini_controls); mSkipBackCtrl->setVisible(has_focus && !mini_controls); mSkipBackCtrl->setEnabled(has_focus && !mini_controls); mVolumeCtrl->setVisible(has_focus); mVolumeCtrl->setEnabled(has_focus); mVolumeSliderCtrl->setEnabled(has_focus && shouldVolumeSliderBeVisible()); mVolumeSliderCtrl->setVisible(has_focus && shouldVolumeSliderBeVisible()); mWhitelistIcon->setVisible(false); mSecureLockIcon->setVisible(false); if (mMediaPanelScroll) { mMediaPanelScroll->setVisible(false); mScrollUpCtrl->setVisible(false); mScrollDownCtrl->setVisible(false); mScrollRightCtrl->setVisible(false); mScrollDownCtrl->setVisible(false); } F32 volume = media_impl->getVolume(); // movie's url changed if(mCurrentURL!=mPreviousURL) { mMovieDuration = media_plugin->getDuration(); mPreviousURL = mCurrentURL; } if(mMovieDuration == 0) { mMovieDuration = media_plugin->getDuration(); mMediaPlaySliderCtrl->setValue(0); mMediaPlaySliderCtrl->setEnabled(false); } // TODO: What if it's not fully loaded if(mUpdateSlider && mMovieDuration!= 0) { F64 current_time = media_plugin->getCurrentTime(); F32 percent = current_time / mMovieDuration; mMediaPlaySliderCtrl->setValue(percent); mMediaPlaySliderCtrl->setEnabled(true); } // video vloume if(volume <= 0.0) { mMuteBtn->setToggleState(true); } else if (volume >= 1.0) { mMuteBtn->setToggleState(false); } else { mMuteBtn->setToggleState(false); } switch(result) { case LLPluginClassMediaOwner::MEDIA_PLAYING: mPlayCtrl->setEnabled(FALSE); mPlayCtrl->setVisible(FALSE); mPauseCtrl->setEnabled(TRUE); mPauseCtrl->setVisible(has_focus); break; case LLPluginClassMediaOwner::MEDIA_PAUSED: default: mPauseCtrl->setEnabled(FALSE); mPauseCtrl->setVisible(FALSE); mPlayCtrl->setEnabled(TRUE); mPlayCtrl->setVisible(has_focus); break; } } else // web based { if(media_plugin) { mCurrentURL = media_plugin->getLocation(); } else { mCurrentURL.clear(); } mPlayCtrl->setVisible(FALSE); mPauseCtrl->setVisible(FALSE); mMediaStopCtrl->setVisible(FALSE); mMediaAddressCtrl->setVisible(has_focus && !mini_controls); mMediaAddressCtrl->setEnabled(has_focus && !mini_controls); mMediaPlaySliderPanel->setVisible(FALSE); mMediaPlaySliderPanel->setEnabled(FALSE); mSkipFwdCtrl->setVisible(FALSE); mSkipFwdCtrl->setEnabled(FALSE); mSkipBackCtrl->setVisible(FALSE); mSkipBackCtrl->setEnabled(FALSE); if(media_impl->getVolume() <= 0.0) { mMuteBtn->setToggleState(true); } else { mMuteBtn->setToggleState(false); } if (mMediaPanelScroll) { mMediaPanelScroll->setVisible(has_focus); mScrollUpCtrl->setVisible(has_focus); mScrollDownCtrl->setVisible(has_focus); mScrollRightCtrl->setVisible(has_focus); mScrollDownCtrl->setVisible(has_focus); } // TODO: get the secure lock bool from media plug in std::string prefix = std::string("https://"); std::string test_prefix = mCurrentURL.substr(0, prefix.length()); LLStringUtil::toLower(test_prefix); if(test_prefix == prefix) { mSecureLockIcon->setVisible(has_focus); } if(mCurrentURL!=mPreviousURL) { setCurrentURL(); mPreviousURL = mCurrentURL; } if(result == LLPluginClassMediaOwner::MEDIA_LOADING) { mReloadCtrl->setEnabled(FALSE); mReloadCtrl->setVisible(FALSE); mStopCtrl->setEnabled(TRUE); mStopCtrl->setVisible(has_focus); } else { mReloadCtrl->setEnabled(TRUE); mReloadCtrl->setVisible(has_focus); mStopCtrl->setEnabled(FALSE); mStopCtrl->setVisible(FALSE); } } if(media_plugin) { // // Handle progress bar // if(LLPluginClassMediaOwner::MEDIA_LOADING == media_plugin->getStatus()) { mMediaProgressPanel->setVisible(true); mMediaProgressBar->setValue(media_plugin->getProgressPercent()); } else { mMediaProgressPanel->setVisible(false); } } if(media_impl) { // // Handle Scrolling // switch (mScrollState) { case SCROLL_UP: media_impl->scrollWheel(0, -1, MASK_NONE); break; case SCROLL_DOWN: media_impl->scrollWheel(0, 1, MASK_NONE); break; case SCROLL_LEFT: media_impl->scrollWheel(1, 0, MASK_NONE); // media_impl->handleKeyHere(KEY_LEFT, MASK_NONE); break; case SCROLL_RIGHT: media_impl->scrollWheel(-1, 0, MASK_NONE); // media_impl->handleKeyHere(KEY_RIGHT, MASK_NONE); break; case SCROLL_NONE: default: break; } } setVisible(enabled); // // Calculate position and shape of the controls // std::vector<LLVector3>::iterator vert_it; std::vector<LLVector3>::iterator vert_end; std::vector<LLVector3> vect_face; LLVolume* volume = objectp->getVolume(); if (volume) { const LLVolumeFace& vf = volume->getVolumeFace(mTargetObjectFace); LLVector3 ext[2]; ext[0].set(vf.mExtents[0].getF32ptr()); ext[1].set(vf.mExtents[1].getF32ptr()); LLVector3 center = (ext[0]+ext[1])*0.5f; LLVector3 size = (ext[1]-ext[0])*0.5f; LLVector3 vert[] = { center + size.scaledVec(LLVector3(1,1,1)), center + size.scaledVec(LLVector3(-1,1,1)), center + size.scaledVec(LLVector3(1,-1,1)), center + size.scaledVec(LLVector3(-1,-1,1)), center + size.scaledVec(LLVector3(1,1,-1)), center + size.scaledVec(LLVector3(-1,1,-1)), center + size.scaledVec(LLVector3(1,-1,-1)), center + size.scaledVec(LLVector3(-1,-1,-1)), }; LLVOVolume* vo = (LLVOVolume*) objectp; for (U32 i = 0; i < 8; i++) { vect_face.push_back(vo->volumePositionToAgent(vert[i])); } } vert_it = vect_face.begin(); vert_end = vect_face.end(); LLMatrix4a mat; if (!is_hud) { mat.setMul(glh_get_current_projection(),glh_get_current_modelview()); } else { LLMatrix4a proj, modelview; if (get_hud_matrices(proj, modelview)) { //mat = proj * modelview; mat.setMul(proj,modelview); } } LLVector4a min; min.splat(1.f); LLVector4a max; max.splat(-1.f); for(; vert_it != vert_end; ++vert_it) { // project silhouette vertices into screen space LLVector4a screen_vert; screen_vert.load3(vert_it->mV,1.f); mat.perspectiveTransform(screen_vert,screen_vert); // add to screenspace bounding box min.setMin(screen_vert,min); max.setMax(screen_vert,max); } // convert screenspace bbox to pixels (in screen coords) LLRect window_rect = gViewerWindow->getWorldViewRectScaled(); LLCoordGL screen_min; screen_min.mX = ll_round((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (min.getF32ptr()[VX] + 1.f) * 0.5f); screen_min.mY = ll_round((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (min.getF32ptr()[VY] + 1.f) * 0.5f); LLCoordGL screen_max; screen_max.mX = ll_round((F32)window_rect.mLeft + (F32)window_rect.getWidth() * (max.getF32ptr()[VX] + 1.f) * 0.5f); screen_max.mY = ll_round((F32)window_rect.mBottom + (F32)window_rect.getHeight() * (max.getF32ptr()[VY] + 1.f) * 0.5f); // grow panel so that screenspace bounding box fits inside "media_region" element of panel LLRect media_panel_rect; // Get the height of the controls (less the volume slider) S32 controls_height = mMediaControlsStack->getRect().getHeight() - mVolumeSliderCtrl->getRect().getHeight(); getParent()->screenRectToLocal(LLRect(screen_min.mX, screen_max.mY, screen_max.mX, screen_min.mY), &media_panel_rect); media_panel_rect.mTop += controls_height; // keep all parts of panel on-screen // Area of the top of the world view to avoid putting the controls window_rect.mTop -= mTopWorldViewAvoidZone; // Don't include "spacing" bookends on left & right of the media controls window_rect.mLeft -= mLeftBookend->getRect().getWidth(); window_rect.mRight += mRightBookend->getRect().getWidth(); // Don't include the volume slider window_rect.mBottom -= mVolumeSliderCtrl->getRect().getHeight(); media_panel_rect.intersectWith(window_rect); // clamp to minimum size, keeping rect inside window S32 centerX = media_panel_rect.getCenterX(); S32 centerY = media_panel_rect.getCenterY(); // Shrink screen rect by min width and height, to ensure containment window_rect.stretch(-mMinWidth/2, -mMinHeight/2); window_rect.clampPointToRect(centerX, centerY); media_panel_rect.setCenterAndSize(centerX, centerY, llmax(mMinWidth, media_panel_rect.getWidth()), llmax(mMinHeight, media_panel_rect.getHeight())); // Finally set the size of the panel setShape(media_panel_rect, true); // Test mouse position to see if the cursor is stationary LLCoordWindow cursor_pos_window; getWindow()->getCursorPosition(&cursor_pos_window); // If last pos is not equal to current pos, the mouse has moved // We need to reset the timer, and make sure the panel is visible if(cursor_pos_window.mX != mLastCursorPos.mX || cursor_pos_window.mY != mLastCursorPos.mY || mScrollState != SCROLL_NONE) { mInactivityTimer.start(); mLastCursorPos = cursor_pos_window; } if(isMouseOver() || hasFocus()) { // Never fade the controls if the mouse is over them or they have keyboard focus. mFadeTimer.stop(); } else if(!mClearFaceOnFade && (mInactivityTimer.getElapsedTimeF32() < mInactiveTimeout)) { // Mouse is over the object, but has not been stationary for long enough to fade the UI mFadeTimer.stop(); } else if(! mFadeTimer.getStarted() ) { // we need to start fading the UI (and we have not already started) mFadeTimer.reset(); mFadeTimer.start(); } else { // I don't think this is correct anymore. This is done in draw() after the fade has completed. // setVisible(FALSE); } } }
void LLDrawable::shiftPos(const LLVector4a &shift_vector) { if (isDead()) { LL_WARNS() << "Shifting dead drawable" << LL_ENDL; return; } if (mParent) { mXform.setPosition(mVObjp->getPosition()); } else { mXform.setPosition(mVObjp->getPositionAgent()); } mXform.updateMatrix(); if (isStatic()) { LLVOVolume* volume = getVOVolume(); bool rebuild = (!volume && getRenderType() != LLPipeline::RENDER_TYPE_TREE && getRenderType() != LLPipeline::RENDER_TYPE_TERRAIN && getRenderType() != LLPipeline::RENDER_TYPE_SKY && getRenderType() != LLPipeline::RENDER_TYPE_GROUND); if (rebuild) { gPipeline.markRebuild(this, LLDrawable::REBUILD_ALL, TRUE); } for (S32 i = 0; i < getNumFaces(); i++) { LLFace *facep = getFace(i); if (facep) { facep->mCenterAgent += LLVector3(shift_vector.getF32ptr()); facep->mExtents[0].add(shift_vector); facep->mExtents[1].add(shift_vector); if (rebuild && facep->hasGeometry()) { facep->clearVertexBuffer(); } } } mExtents[0].add(shift_vector); mExtents[1].add(shift_vector); mPositionGroup.add(shift_vector); } else if (mSpatialBridge) { mSpatialBridge->shiftPos(shift_vector); } else if (isAvatar()) { mExtents[0].add(shift_vector); mExtents[1].add(shift_vector); mPositionGroup.add(shift_vector); } mVObjp->onShift(shift_vector); }
// Shrink the model to fit // on a 1x1x1 cube centered at the origin. // The positions and extents // multiplied by mNormalizedScale // and offset by mNormalizedTranslation // to be the "original" extents and position. // Also, the positions will fit // within the unit cube. void LLModel::normalizeVolumeFaces() { // ensure we don't have too many faces if (mVolumeFaces.size() > LL_SCULPT_MESH_MAX_FACES) mVolumeFaces.resize(LL_SCULPT_MESH_MAX_FACES); if (!mVolumeFaces.empty()) { LLVector4a min, max; // For all of the volume faces // in the model, loop over // them and see what the extents // of the volume along each axis. min = mVolumeFaces[0].mExtents[0]; max = mVolumeFaces[0].mExtents[1]; for (U32 i = 1; i < mVolumeFaces.size(); ++i) { LLVolumeFace& face = mVolumeFaces[i]; update_min_max(min, max, face.mExtents[0]); update_min_max(min, max, face.mExtents[1]); if (face.mTexCoords) { LLVector2& min_tc = face.mTexCoordExtents[0]; LLVector2& max_tc = face.mTexCoordExtents[1]; min_tc = face.mTexCoords[0]; max_tc = face.mTexCoords[0]; for (U32 j = 1; j < (U32)face.mNumVertices; ++j) { update_min_max(min_tc, max_tc, face.mTexCoords[j]); } } else { face.mTexCoordExtents[0].set(0,0); face.mTexCoordExtents[1].set(1,1); } } // Now that we have the extents of the model // we can compute the offset needed to center // the model at the origin. // Compute center of the model // and make it negative to get translation // needed to center at origin. LLVector4a trans; trans.setAdd(min, max); trans.mul(-0.5f); // Compute the total size along all // axes of the model. LLVector4a size; size.setSub(max, min); // Prevent division by zero. F32 x = size[0]; F32 y = size[1]; F32 z = size[2]; F32 w = size[3]; if (fabs(x)<F_APPROXIMATELY_ZERO) { x = 1.0; } if (fabs(y)<F_APPROXIMATELY_ZERO) { y = 1.0; } if (fabs(z)<F_APPROXIMATELY_ZERO) { z = 1.0; } size.set(x,y,z,w); // Compute scale as reciprocal of size LLVector4a scale; scale.splat(1.f); scale.div(size); LLVector4a inv_scale(1.f); inv_scale.div(scale); for (U32 i = 0; i < mVolumeFaces.size(); ++i) { LLVolumeFace& face = mVolumeFaces[i]; // We shrink the extents so // that they fall within // the unit cube. face.mExtents[0].add(trans); face.mExtents[0].mul(scale); face.mExtents[1].add(trans); face.mExtents[1].mul(scale); // For all the positions, we scale // the positions to fit within the unit cube. LLVector4a* pos = (LLVector4a*) face.mPositions; LLVector4a* norm = (LLVector4a*) face.mNormals; for (U32 j = 0; j < (U32)face.mNumVertices; ++j) { pos[j].add(trans); pos[j].mul(scale); if (norm && !norm[j].equals3(LLVector4a::getZero())) { norm[j].mul(inv_scale); norm[j].normalize3(); } } } // mNormalizedScale is the scale at which // we would need to multiply the model // by to get the original size of the // model instead of the normalized size. LLVector4a normalized_scale; normalized_scale.splat(1.f); normalized_scale.div(scale); mNormalizedScale.set(normalized_scale.getF32ptr()); mNormalizedTranslation.set(trans.getF32ptr()); mNormalizedTranslation *= -1.f; } }
void LLOcclusionCullingGroup::doOcclusion(LLCamera* camera, const LLVector4a* shift) { LLGLDisable stencil(GL_STENCIL_TEST); if (mSpatialPartition->isOcclusionEnabled() && LLPipeline::sUseOcclusion > 1) { //move mBounds to the agent space if necessary LLVector4a bounds[2]; bounds[0] = mBounds[0]; bounds[1] = mBounds[1]; if(shift != NULL) { bounds[0].add(*shift); } // Don't cull hole/edge water, unless we have the GL_ARB_depth_clamp extension if (earlyFail(camera, bounds)) { LLFastTimer t(FTM_OCCLUSION_EARLY_FAIL); setOcclusionState(LLOcclusionCullingGroup::DISCARD_QUERY); assert_states_valid(this); clearOcclusionState(LLOcclusionCullingGroup::OCCLUDED, LLOcclusionCullingGroup::STATE_MODE_DIFF); assert_states_valid(this); } else { if (!isOcclusionState(QUERY_PENDING) || isOcclusionState(DISCARD_QUERY)) { { //no query pending, or previous query to be discarded LLFastTimer t(FTM_RENDER_OCCLUSION); if (!mOcclusionQuery[LLViewerCamera::sCurCameraID]) { LLFastTimer t(FTM_OCCLUSION_ALLOCATE); mOcclusionQuery[LLViewerCamera::sCurCameraID] = getNewOcclusionQueryObjectName(); } // Depth clamp all water to avoid it being culled as a result of being // behind the far clip plane, and in the case of edge water to avoid // it being culled while still visible. bool const use_depth_clamp = gGLManager.mHasDepthClamp && (mSpatialPartition->mDrawableType == LLDrawPool::POOL_WATER || mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER); LLGLEnable clamp(use_depth_clamp ? GL_DEPTH_CLAMP : 0); #if !LL_DARWIN U32 mode = gGLManager.mHasOcclusionQuery2 ? GL_ANY_SAMPLES_PASSED : GL_SAMPLES_PASSED_ARB; #else U32 mode = GL_SAMPLES_PASSED_ARB; #endif #if LL_TRACK_PENDING_OCCLUSION_QUERIES sPendingQueries.insert(mOcclusionQuery[LLViewerCamera::sCurCameraID]); #endif { LLFastTimer t(FTM_PUSH_OCCLUSION_VERTS); //store which frame this query was issued on mOcclusionIssued[LLViewerCamera::sCurCameraID] = gFrameCount; { LLFastTimer t(FTM_OCCLUSION_BEGIN_QUERY); glBeginQueryARB(mode, mOcclusionQuery[LLViewerCamera::sCurCameraID]); } LLGLSLShader* shader = LLGLSLShader::sCurBoundShaderPtr; llassert(shader); shader->uniform3fv(LLShaderMgr::BOX_CENTER, 1, bounds[0].getF32ptr()); //static LLVector4a fudge(SG_OCCLUSION_FUDGE); static LLCachedControl<F32> vel("SHOcclusionFudge",SG_OCCLUSION_FUDGE); LLVector4a fudge(SG_OCCLUSION_FUDGE); static LLVector4a fudged_bounds; fudged_bounds.setAdd(fudge, bounds[1]); shader->uniform3fv(LLShaderMgr::BOX_SIZE, 1, fudged_bounds.getF32ptr()); if (!use_depth_clamp && mSpatialPartition->mDrawableType == LLDrawPool::POOL_VOIDWATER) { LLFastTimer t(FTM_OCCLUSION_DRAW_WATER); LLGLSquashToFarClip squash(glh_get_current_projection(), 1); if (camera->getOrigin().isExactlyZero()) { //origin is invalid, draw entire box gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0); gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8); } else { gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, bounds[0])); } } else { LLFastTimer t(FTM_OCCLUSION_DRAW); if (camera->getOrigin().isExactlyZero()) { //origin is invalid, draw entire box gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, 0); gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, b111*8); } else { gPipeline.mCubeVB->drawRange(LLRender::TRIANGLE_FAN, 0, 7, 8, get_box_fan_indices(camera, bounds[0])); } } { LLFastTimer t(FTM_OCCLUSION_END_QUERY); glEndQueryARB(mode); } } } { LLFastTimer t(FTM_SET_OCCLUSION_STATE); setOcclusionState(LLOcclusionCullingGroup::QUERY_PENDING); clearOcclusionState(LLOcclusionCullingGroup::DISCARD_QUERY); } } } } }
BOOL LLFace::genVolumeBBoxes(const LLVolume &volume, S32 f, const LLMatrix4& mat_vert_in, const LLMatrix3& mat_normal_in, BOOL global_volume) { LLMemType mt1(LLMemType::MTYPE_DRAWABLE); //get bounding box if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME | LLDrawable::REBUILD_POSITION #if MESH_ENABLED | LLDrawable::REBUILD_RIGGED #endif //MESH_ENABLED )) { //VECTORIZE THIS LLMatrix4a mat_vert; mat_vert.loadu(mat_vert_in); LLMatrix4a mat_normal; mat_normal.loadu(mat_normal_in); //if (mDrawablep->isState(LLDrawable::REBUILD_VOLUME)) //{ //vertex buffer no longer valid // mVertexBuffer = NULL; // mLastVertexBuffer = NULL; //} //VECTORIZE THIS LLVector4a min,max; if (f >= volume.getNumVolumeFaces()) { llwarns << "Generating bounding box for invalid face index!" << llendl; f = 0; } const LLVolumeFace &face = volume.getVolumeFace(f); min = face.mExtents[0]; max = face.mExtents[1]; llassert(less_than_max_mag(min)); llassert(less_than_max_mag(max)); //min, max are in volume space, convert to drawable render space LLVector4a center; LLVector4a t; t.setAdd(min, max); t.mul(0.5f); mat_vert.affineTransform(t, center); LLVector4a size; size.setSub(max, min); size.mul(0.5f); llassert(less_than_max_mag(min)); llassert(less_than_max_mag(max)); if (!global_volume) { //VECTORIZE THIS LLVector4a scale; scale.load3(mDrawablep->getVObj()->getScale().mV); size.mul(scale); } mat_normal.mMatrix[0].normalize3fast(); mat_normal.mMatrix[1].normalize3fast(); mat_normal.mMatrix[2].normalize3fast(); LLVector4a v[4]; //get 4 corners of bounding box mat_normal.rotate(size,v[0]); //VECTORIZE THIS LLVector4a scale; scale.set(-1.f, -1.f, 1.f); scale.mul(size); mat_normal.rotate(scale, v[1]); scale.set(1.f, -1.f, -1.f); scale.mul(size); mat_normal.rotate(scale, v[2]); scale.set(-1.f, 1.f, -1.f); scale.mul(size); mat_normal.rotate(scale, v[3]); LLVector4a& newMin = mExtents[0]; LLVector4a& newMax = mExtents[1]; newMin = newMax = center; llassert(less_than_max_mag(center)); for (U32 i = 0; i < 4; i++) { LLVector4a delta; delta.setAbs(v[i]); LLVector4a min; min.setSub(center, delta); LLVector4a max; max.setAdd(center, delta); newMin.setMin(newMin,min); newMax.setMax(newMax,max); llassert(less_than_max_mag(newMin)); llassert(less_than_max_mag(newMax)); } if (!mDrawablep->isActive()) { LLVector4a offset; offset.load3(mDrawablep->getRegion()->getOriginAgent().mV); newMin.add(offset); newMax.add(offset); llassert(less_than_max_mag(newMin)); llassert(less_than_max_mag(newMax)); } t.setAdd(newMin, newMax); t.mul(0.5f); llassert(less_than_max_mag(t)); //VECTORIZE THIS mCenterLocal.set(t.getF32ptr()); llassert(less_than_max_mag(newMin)); llassert(less_than_max_mag(newMax)); t.setSub(newMax,newMin); mBoundingSphereRadius = t.getLength3().getF32()*0.5f; updateCenterAgent(); } return TRUE; }
BOOL LLFace::getGeometryVolume(const LLVolume& volume, const S32 &f, const LLMatrix4& mat_vert_in, const LLMatrix3& mat_norm_in, const U16 &index_offset, bool force_rebuild) { llassert(verify()); const LLVolumeFace &vf = volume.getVolumeFace(f); S32 num_vertices = (S32)vf.mNumVertices; S32 num_indices = (S32) vf.mNumIndices; if (mVertexBuffer.notNull()) { if (num_indices + (S32) mIndicesIndex > mVertexBuffer->getNumIndices()) { llwarns << "Index buffer overflow!" << llendl; llwarns << "Indices Count: " << mIndicesCount << " VF Num Indices: " << num_indices << " Indices Index: " << mIndicesIndex << " VB Num Indices: " << mVertexBuffer->getNumIndices() << llendl; llwarns << "Last Indices Count: " << mLastIndicesCount << " Last Indices Index: " << mLastIndicesIndex << " Face Index: " << f << " Pool Type: " << mPoolType << llendl; return FALSE; } if (num_vertices + mGeomIndex > mVertexBuffer->getNumVerts()) { llwarns << "Vertex buffer overflow!" << llendl; return FALSE; } } LLStrider<LLVector3> vertices; LLStrider<LLVector2> tex_coords; LLStrider<LLVector2> tex_coords2; LLStrider<LLVector3> normals; LLStrider<LLColor4U> colors; LLStrider<LLVector3> binormals; LLStrider<U16> indicesp; #if MESH_ENABLED LLStrider<LLVector4> weights; #endif //MESH_ENABLED BOOL full_rebuild = force_rebuild || mDrawablep->isState(LLDrawable::REBUILD_VOLUME); BOOL global_volume = mDrawablep->getVOVolume()->isVolumeGlobal(); LLVector3 scale; if (global_volume) { scale.setVec(1,1,1); } else { scale = mVObjp->getScale(); } bool rebuild_pos = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_POSITION); bool rebuild_color = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_COLOR); bool rebuild_tcoord = full_rebuild || mDrawablep->isState(LLDrawable::REBUILD_TCOORD); bool rebuild_normal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); bool rebuild_binormal = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_BINORMAL); #if MESH_ENABLED bool rebuild_weights = rebuild_pos && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_WEIGHT4); #endif //MESH_ENABLED const LLTextureEntry *tep = mVObjp->getTE(f); if (!tep) rebuild_color = FALSE; // can't get color when tep is NULL U8 bump_code = tep ? tep->getBumpmap() : 0; BOOL is_static = mDrawablep->isStatic(); BOOL is_global = is_static; LLVector3 center_sum(0.f, 0.f, 0.f); if (is_global) { setState(GLOBAL); } else { clearState(GLOBAL); } LLColor4U color = (tep ? LLColor4U(tep->getColor()) : LLColor4U::white); if (rebuild_color) // FALSE if tep == NULL { if (tep) { GLfloat alpha[4] = { 0.00f, 0.25f, 0.5f, 0.75f }; if (getPoolType() != LLDrawPool::POOL_ALPHA && (LLPipeline::sRenderDeferred || (LLPipeline::sRenderBump && tep->getShiny()))) { color.mV[3] = U8 (alpha[tep->getShiny()] * 255); } } } // INDICES if (full_rebuild) { mVertexBuffer->getIndexStrider(indicesp, mIndicesIndex); for (U32 i = 0; i < (U32) num_indices; i++) { indicesp[i] = vf.mIndices[i] + index_offset; } //mVertexBuffer->setBuffer(0); } LLMatrix4a mat_normal; mat_normal.loadu(mat_norm_in); //if it's not fullbright and has no normals, bake sunlight based on face normal //bool bake_sunlight = !getTextureEntry()->getFullbright() && // !mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_NORMAL); F32 r = 0, os = 0, ot = 0, ms = 0, mt = 0, cos_ang = 0, sin_ang = 0; if (rebuild_tcoord) { bool do_xform; if (tep) { r = tep->getRotation(); os = tep->mOffsetS; ot = tep->mOffsetT; ms = tep->mScaleS; mt = tep->mScaleT; cos_ang = cos(r); sin_ang = sin(r); if (cos_ang != 1.f || sin_ang != 0.f || os != 0.f || ot != 0.f || ms != 1.f || mt != 1.f) { do_xform = true; } else { do_xform = false; } } else { do_xform = false; } //bump setup LLVector4a binormal_dir( -sin_ang, cos_ang, 0.f ); LLVector4a bump_s_primary_light_ray(0.f, 0.f, 0.f); LLVector4a bump_t_primary_light_ray(0.f, 0.f, 0.f); LLQuaternion bump_quat; if (mDrawablep->isActive()) { bump_quat = LLQuaternion(mDrawablep->getRenderMatrix()); } if (bump_code) { mVObjp->getVolume()->genBinormals(f); F32 offset_multiple; switch( bump_code ) { case BE_NO_BUMP: offset_multiple = 0.f; break; case BE_BRIGHTNESS: case BE_DARKNESS: if( mTexture.notNull() && mTexture->hasGLTexture()) { // Offset by approximately one texel S32 cur_discard = mTexture->getDiscardLevel(); S32 max_size = llmax( mTexture->getWidth(), mTexture->getHeight() ); max_size <<= cur_discard; const F32 ARTIFICIAL_OFFSET = 2.f; offset_multiple = ARTIFICIAL_OFFSET / (F32)max_size; } else { offset_multiple = 1.f/256; } break; default: // Standard bumpmap textures. Assumed to be 256x256 offset_multiple = 1.f / 256; break; } F32 s_scale = 1.f; F32 t_scale = 1.f; if( tep ) { tep->getScale( &s_scale, &t_scale ); } // Use the nudged south when coming from above sun angle, such // that emboss mapping always shows up on the upward faces of cubes when // it's noon (since a lot of builders build with the sun forced to noon). LLVector3 sun_ray = gSky.mVOSkyp->mBumpSunDir; LLVector3 moon_ray = gSky.getMoonDirection(); LLVector3& primary_light_ray = (sun_ray.mV[VZ] > 0) ? sun_ray : moon_ray; bump_s_primary_light_ray.load3((offset_multiple * s_scale * primary_light_ray).mV); bump_t_primary_light_ray.load3((offset_multiple * t_scale * primary_light_ray).mV); } U8 texgen = getTextureEntry()->getTexGen(); if (rebuild_tcoord && texgen != LLTextureEntry::TEX_GEN_DEFAULT) { //planar texgen needs binormals mVObjp->getVolume()->genBinormals(f); } U8 tex_mode = 0; if (isState(TEXTURE_ANIM)) { LLVOVolume* vobj = (LLVOVolume*) (LLViewerObject*) mVObjp; tex_mode = vobj->mTexAnimMode; if (!tex_mode) { clearState(TEXTURE_ANIM); } else { os = ot = 0.f; r = 0.f; cos_ang = 1.f; sin_ang = 0.f; ms = mt = 1.f; do_xform = false; } if (getVirtualSize() >= MIN_TEX_ANIM_SIZE) { //don't override texture transform during tc bake tex_mode = 0; } } LLVector4a scalea; scalea.load3(scale.mV); bool do_bump = bump_code && mVertexBuffer->hasDataType(LLVertexBuffer::TYPE_TEXCOORD1); bool do_tex_mat = tex_mode && mTextureMatrix; if (!do_bump) { //not in atlas or not bump mapped, might be able to do a cheap update mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex); if (texgen != LLTextureEntry::TEX_GEN_PLANAR) { if (!do_tex_mat) { if (!do_xform) { tex_coords.assignArray((U8*) vf.mTexCoords, sizeof(vf.mTexCoords[0]), num_vertices); } else { for (S32 i = 0; i < num_vertices; i++) { LLVector2 tc(vf.mTexCoords[i]); xform(tc, cos_ang, sin_ang, os, ot, ms, mt); *tex_coords++ = tc; } } } else { //do tex mat, no texgen, no atlas, no bump for (S32 i = 0; i < num_vertices; i++) { LLVector2 tc(vf.mTexCoords[i]); //LLVector4a& norm = vf.mNormals[i]; //LLVector4a& center = *(vf.mCenter); LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f); tmp = tmp * *mTextureMatrix; tc.mV[0] = tmp.mV[0]; tc.mV[1] = tmp.mV[1]; *tex_coords++ = tc; } } } else { //no bump, no atlas, tex gen planar if (do_tex_mat) { for (S32 i = 0; i < num_vertices; i++) { LLVector2 tc(vf.mTexCoords[i]); LLVector4a& norm = vf.mNormals[i]; LLVector4a& center = *(vf.mCenter); LLVector4a vec = vf.mPositions[i]; vec.mul(scalea); planarProjection(tc, norm, center, vec); LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f); tmp = tmp * *mTextureMatrix; tc.mV[0] = tmp.mV[0]; tc.mV[1] = tmp.mV[1]; *tex_coords++ = tc; } } else { for (S32 i = 0; i < num_vertices; i++) { LLVector2 tc(vf.mTexCoords[i]); LLVector4a& norm = vf.mNormals[i]; LLVector4a& center = *(vf.mCenter); LLVector4a vec = vf.mPositions[i]; vec.mul(scalea); planarProjection(tc, norm, center, vec); xform(tc, cos_ang, sin_ang, os, ot, ms, mt); *tex_coords++ = tc; } } } //mVertexBuffer->setBuffer(0); } else { //either bump mapped or in atlas, just do the whole expensive loop mVertexBuffer->getTexCoord0Strider(tex_coords, mGeomIndex); std::vector<LLVector2> bump_tc; for (S32 i = 0; i < num_vertices; i++) { LLVector2 tc(vf.mTexCoords[i]); LLVector4a& norm = vf.mNormals[i]; LLVector4a& center = *(vf.mCenter); if (texgen != LLTextureEntry::TEX_GEN_DEFAULT) { LLVector4a vec = vf.mPositions[i]; vec.mul(scalea); switch (texgen) { case LLTextureEntry::TEX_GEN_PLANAR: planarProjection(tc, norm, center, vec); break; case LLTextureEntry::TEX_GEN_SPHERICAL: sphericalProjection(tc, norm, center, vec); break; case LLTextureEntry::TEX_GEN_CYLINDRICAL: cylindricalProjection(tc, norm, center, vec); break; default: break; } } if (tex_mode && mTextureMatrix) { LLVector3 tmp(tc.mV[0], tc.mV[1], 0.f); tmp = tmp * *mTextureMatrix; tc.mV[0] = tmp.mV[0]; tc.mV[1] = tmp.mV[1]; } else { xform(tc, cos_ang, sin_ang, os, ot, ms, mt); } *tex_coords++ = tc; if (do_bump) { bump_tc.push_back(tc); } } //mVertexBuffer->setBuffer(0); if (do_bump) { mVertexBuffer->getTexCoord1Strider(tex_coords2, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { LLVector4a tangent; tangent.setCross3(vf.mBinormals[i], vf.mNormals[i]); LLMatrix4a tangent_to_object; tangent_to_object.setRows(tangent, vf.mBinormals[i], vf.mNormals[i]); LLVector4a t; tangent_to_object.rotate(binormal_dir, t); LLVector4a binormal; mat_normal.rotate(t, binormal); //VECTORIZE THIS if (mDrawablep->isActive()) { LLVector3 t; t.set(binormal.getF32ptr()); t *= bump_quat; binormal.load3(t.mV); } binormal.normalize3fast(); LLVector2 tc = bump_tc[i]; tc += LLVector2( bump_s_primary_light_ray.dot3(tangent).getF32(), bump_t_primary_light_ray.dot3(binormal).getF32() ); *tex_coords2++ = tc; } //mVertexBuffer->setBuffer(0); } } } if (rebuild_pos) { llassert(num_vertices > 0); mVertexBuffer->getVertexStrider(vertices, mGeomIndex); LLMatrix4a mat_vert; mat_vert.loadu(mat_vert_in); LLVector4a* src = vf.mPositions; LLVector4a position; for (S32 i = 0; i < num_vertices; i++) { mat_vert.affineTransform(src[i], position); vertices[i].set(position.getF32ptr()); } //mVertexBuffer->setBuffer(0); } if (rebuild_normal) { mVertexBuffer->getNormalStrider(normals, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { LLVector4a normal; mat_normal.rotate(vf.mNormals[i], normal); normal.normalize3fast(); normals[i].set(normal.getF32ptr()); } //mVertexBuffer->setBuffer(0); } if (rebuild_binormal) { mVertexBuffer->getBinormalStrider(binormals, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { LLVector4a binormal; mat_normal.rotate(vf.mBinormals[i], binormal); binormal.normalize3fast(); binormals[i].set(binormal.getF32ptr()); } //mVertexBuffer->setBuffer(0); } #if MESH_ENABLED if (rebuild_weights && vf.mWeights) { mVertexBuffer->getWeight4Strider(weights, mGeomIndex); weights.assignArray((U8*) vf.mWeights, sizeof(vf.mWeights[0]), num_vertices); //mVertexBuffer->setBuffer(0); } #endif //MESH_ENABLED if (rebuild_color) { mVertexBuffer->getColorStrider(colors, mGeomIndex); for (S32 i = 0; i < num_vertices; i++) { colors[i] = color; } //mVertexBuffer->setBuffer(0); } if (rebuild_tcoord) { mTexExtents[0].setVec(0,0); mTexExtents[1].setVec(1,1); xform(mTexExtents[0], cos_ang, sin_ang, os, ot, ms, mt); xform(mTexExtents[1], cos_ang, sin_ang, os, ot, ms, mt); F32 es = vf.mTexCoordExtents[1].mV[0] - vf.mTexCoordExtents[0].mV[0] ; F32 et = vf.mTexCoordExtents[1].mV[1] - vf.mTexCoordExtents[0].mV[1] ; mTexExtents[0][0] *= es ; mTexExtents[1][0] *= es ; mTexExtents[0][1] *= et ; mTexExtents[1][1] *= et ; } mLastVertexBuffer = mVertexBuffer; mLastGeomCount = mGeomCount; mLastGeomIndex = mGeomIndex; mLastIndicesCount = mIndicesCount; mLastIndicesIndex = mIndicesIndex; return TRUE; }