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;
}
          bool set_conditions(const std::vector<rib_data_type> &rbs, const std::vector<index_type> &maxd, bool cl=false)
          {
            index_type i, j, nsegs(static_cast<index_type>(maxd.size())), nribs(rbs.size());

            // ensure input vectors are correct size
            if (!cl && (nribs!=(nsegs+1)))
              return false;
            if (cl && (nribs!=nsegs))
              return false;

            // check to make sure have valid end conditions
            if (!cl)
            {
              if (rbs[0].use_left_fp() || rbs[0].use_left_fpp() || rbs[0].get_continuity()!=rib_data_type::C0)
              {
                return false;
              }
              if (rbs[nsegs].use_right_fp() || rbs[nsegs].use_right_fpp() || rbs[nsegs].get_continuity()!=rib_data_type::C0)
              {
                return false;
              }
            }

            // make sure ribs are in valid state
            data_type v_start(rbs[0].get_t0()), v_end(rbs[0].get_tmax());
            tolerance_type tol;
            for (i=0; i<nribs; ++i)
            {
              if (!rbs[i].check_state())
                return false;
              if (!tol.approximately_equal(rbs[i].get_t0(), v_start) || !tol.approximately_equal(rbs[i].get_tmax(), v_end))
                return false;
            }

            // find all unique v-coordinates on joints for each rib
            auto comp = [&tol](const data_type &x1, const data_type &x2)->bool
            {
              return tol.approximately_less_than(x1, x2);
            };

            std::vector<data_type> joints;
            data_type t0(rbs[0].get_t0()), tmax(rbs[0].get_tmax());

            rbs[0].get_joints(std::back_inserter(joints));
            for (i=1; i<nribs; ++i)
            {
              // test to make sure this rib's parameterization matches rest
              if (!tol.approximately_equal(rbs[i].get_t0(), t0) || !tol.approximately_equal(rbs[i].get_tmax(), tmax))
              {
                return false;
              }

              // get the joints on the current rib
              std::vector<data_type> rjoints, jts_out;
              rbs[i].get_joints(std::back_inserter(rjoints));

              // merge these joints with current list of joints
              std::set_union(joints.begin(), joints.end(), rjoints.begin(), rjoints.end(), std::back_inserter(jts_out), comp);
              std::swap(joints, jts_out);
            }

            // record where the joints need to be for create()
            index_type njoints(static_cast<index_type>(joints.size()));

            // set the v-parameterization
            this->set_number_v_segments(njoints-1);
            this->set_initial_v(joints[0]);
            for (j=0; j<(njoints-1); ++j)
            {
              this->set_dv(joints[j+1]-joints[j], j);
            }

            // reset the number of u-segments
            this->set_number_u_segments(nsegs);

            ribs=rbs;
            max_degree=maxd;
            closed=cl;

            return true;
          }