/*!
\brief Shrinks the quadrangle by a given orthogonal offset for every edge.
*/
Quadrangle Quadrangle::Shrink(const double& ab,const double& bc, const double& cd, const double& da)
{
  Vector sa = p[0]-Normalized((p[1]-p[0])/Vector(0,0,1))*ab;
  Vector sb = p[1]-Normalized((p[2]-p[1])/Vector(0,0,1))*bc;
  Vector sc = p[2]-Normalized((p[3]-p[2])/Vector(0,0,1))*cd;
  Vector sd = p[3]-Normalized((p[0]-p[3])/Vector(0,0,1))*da;

  return Quadrangle(
    Intersection(sa,sa+(p[1]-p[0]),sb,sb+(p[2]-p[1])),
    Intersection(sb,sb+(p[2]-p[1]),sc,sc+(p[3]-p[2])),
    Intersection(sc,sc+(p[3]-p[2]),sd,sd+(p[0]-p[3])),
    Intersection(sd,sd+(p[0]-p[3]),sa,sa+(p[1]-p[0])));
}
Exemple #2
0
//=============================================================================
Obb2::Obb2 (
    const Point2 & center,
    const Vector2 & unitX,
    const Vector2 & unitY,
    const Vector2 & extent
) :
    center(center),
    extent(extent)
{
    ASSERT(Normalized(unitX));
    ASSERT(Normalized(unitY));
    
    u[0] = unitX;
    u[1] = unitY;
}
Pentangle Pentangle::Shrink(const double& ab,const double& bc, const double& cd, const double& de, const double& ea)
{
  Vector sa = p[0]-Normalized((p[1]-p[0])/Vector(0,0,1))*ab;
  Vector sb = p[1]-Normalized((p[2]-p[1])/Vector(0,0,1))*bc;
  Vector sc = p[2]-Normalized((p[3]-p[2])/Vector(0,0,1))*cd;
  Vector sd = p[3]-Normalized((p[4]-p[3])/Vector(0,0,1))*de;
  Vector se = p[4]-Normalized((p[0]-p[4])/Vector(0,0,1))*ea;

  return Pentangle(
    Intersection(sa,sa+(p[1]-p[0]),sb,sb+(p[2]-p[1])),
    Intersection(sb,sb+(p[2]-p[1]),sc,sc+(p[3]-p[2])),
    Intersection(sc,sc+(p[3]-p[2]),sd,sd+(p[4]-p[3])),
    Intersection(sd,sd+(p[4]-p[3]),se,se+(p[0]-p[4])),
	Intersection(se,se+(p[0]-p[4]),sa,sa+(p[1]-p[0])));
}
Exemple #4
0
Quaternion::Quaternion(Matrix4 &mat) {
	float tr = mat(0, 0) + mat(1, 1) + mat(2, 2);

	if (tr > 0.0f) {
		float s = sqrtf(tr + 1.0f) * 2.0f;
		m_w = 0.25f * s;
		m_x = (mat(2, 1) - mat(1, 2)) / s;
		m_y = (mat(0, 2) - mat(2, 0)) / s;
		m_z = (mat(1, 0) - mat(0, 1)) / s;
	} else {
		if (mat(0, 0) > mat(1, 1) && mat(0, 0) > mat(2, 2)) {
			float s = sqrtf(1.0f + mat(0, 0) - mat(1, 1) - mat(2, 2)) * 2.0f;
			m_w = (mat(2, 1) - mat(1, 2)) / s;
			m_x = 0.25f * s;
			m_y = (mat(0, 1) + mat(1, 0)) / s;
			m_z = (mat(0, 2) + mat(2, 0)) / s;
		} else if (mat(1, 1) > mat(2, 2)) {
			float s = sqrtf(1.0f + mat(1, 1) - mat(0, 0) - mat(2, 2)) * 2.0f;
			m_w = (mat(0, 2) - mat(2, 0)) / s;
			m_x = (mat(0, 1) + mat(1, 0)) / s;
			m_y = 0.25f * s;
			m_z = (mat(1, 2) + mat(2, 1)) / s;
		} else {
			float s = sqrtf(1.0f + mat(2, 2) - mat(0, 0) - mat(1, 1)) * 2.0f;
			m_w = (mat(1, 0) - mat(0, 1)) / s;
			m_x = (mat(0, 2) + mat(2, 0)) / s;
			m_y = (mat(1, 2) + mat(2, 1)) / s;
			m_z = 0.25f * s;
		}
	}

	(*this) = Normalized();
}
	void RenderGlassShadowMap(
		const Vec3f& light_position,
		const Vec3f& torus_center,
		const Mat4f& torus_matrix,
		const Mat4f& light_proj_matrix
	)
	{
		glass_shadow_fbo.Bind(Framebuffer::Target::Draw);

		gl.Viewport(shadow_tex_side, shadow_tex_side);
		const GLfloat clear_color[4] = {1.0f, 1.0f, 1.0f, 0.0f};
		gl.ClearColorBuffer(0, clear_color);

		transf_prog.camera_matrix.Set(light_proj_matrix);
		transf_prog.camera_position.Set(light_position);
		transf_prog.light_proj_matrix.Set(light_proj_matrix);
		transf_prog.light_position.Set(light_position);

		// Render the torus' frame
		transf_prog.model_matrix.Set(torus_matrix);

		// setup the view clipping plane
		Planef clip_plane = Planef::FromPointAndNormal(
			torus_center,
			Normalized(light_position-torus_center)
		);
		transf_prog.clip_plane.Set(clip_plane.Equation());

		light_pp.Bind();

		light_prog.color = Vec3f(0.6f, 0.4f, 0.1f);

		gl.Disable(Capability::DepthTest);
		gl.Enable(Functionality::ClipDistance, 0);
		gl.Enable(Capability::Blend);

		for(int c=0; c!=2; ++c)
		{
			transf_prog.clip_direction.Set((c == 0)?1:-1);

			for(int p=3; p>=0; --p)
			{
				if(p % 2 == 0) gl.CullFace(Face::Front);
				else gl.CullFace(Face::Back);
				torus.Draw(
					[&p](GLuint phase) -> bool
					{
						if(p == 0 || p == 3)
						{
							return (phase == 4);
						}
						else return (phase > 4);
					}
				);
			}
		}
		gl.Disable(Capability::Blend);
		gl.Disable(Functionality::ClipDistance, 0);
		gl.Enable(Capability::DepthTest);
	}
void Quadrangle::shrink(const float &l)
{

    Vector2D tab[4];

    for(int i=0; i<4; i++){
        Vector2D orth1 = Orthogonal(-((*this)[(i+1)%4]-(*this)[i])).normalise();
        Vector2D orth2 = Orthogonal( ((*this)[(i-1)%4]-(*this)[i])).normalise();

        std::cout << (*this)[i] << " :: " << (*this)[(i+1)%4] << std::endl;
        std::cout << (*this)[(i+1)%4]-(*this)[i] << std::endl;
        std::cout << "orth1 : " << orth1 << std::endl;

        std::cout << (*this)[i] << " :: " << (*this)[(i-1)%4] << std::endl;
        std::cout << ((*this)[(i-1)%4]-(*this)[i]) << std::endl;
        std::cout << "orth2 : " << orth2 << std::endl;
        std::cout << "tab["<<i<<"] : " << tab[i] << std::endl;

        Vector2D v = (orth1 + orth2)/2;

        std::cout << "v.Norm() : " << v.getNorm() << std::endl;
        std::cout << std::endl;

        tab[i] = (*this)[i]+Normalized(v)*(l/(v.getNorm()));
    }

    for(int i=0; i<4; i++){
        std::cout << "tab["<<i<<"] : " << tab[i] << std::endl;
        (*this)[i] = tab[i];
    }
}
Exemple #7
0
void EnemyShip::changeVelocity(const float& dt, Engine::ParticleSystem& system)
{
	if (target != nullptr)
	{
		Vector2 toTarget = target->getPosition() - shipPosition;
		
		if (LengthSquared(toTarget) != 0)
		{
			Vector2 normalizedtoTarget = PerpCCW(Normalized(toTarget));

			shipRotation = atan2f(normalizedtoTarget.getY(), normalizedtoTarget.getX());

			if (LengthSquared(toTarget) >= 0)
			{
				velocity = velocity + (Engine::Matrix2::rotation(shipRotation) * Engine::Vector2(0, -(acceleration * dt) * dt));
				system.AddParticle(new THRUSTPARTICLEUP);
			}
			else
			{
				velocity = velocity + (Engine::Matrix2::rotation(shipRotation) * Engine::Vector2(0, (acceleration * dt) * dt));
				system.AddParticle(new THRUSTPARTICLEDOWN);
			}
		}
	}
}
oglplus::Vec3f SpectraDocumentView::PickOnSphere(GLint x, GLint y)
{
	oglplus::Vec3f ori(target_x, target_y, target_z);
	oglplus::Vec3f cam = camera_matrix.Position();
	oglplus::Vec3f ray = Normalized(ScreenToWorld(x, y) - cam);
	oglplus::Vec3f ofs = (cam-ori);
	GLfloat rad = camera_distance*Tan(camera_xfov*0.5f);

	GLfloat a = 1.0f; //Dot(ray, ray);
	GLfloat b = 2*Dot(ofs, ray);
	GLfloat c = Dot(ofs, ofs)-rad*rad;
	GLfloat d = b*b-4*a*c;

	if(d >= 0.0f)
	{
		GLfloat q = 0.5f/a;

		GLfloat t0 = (-b-std::sqrt(d))*q;
		GLfloat t1 = (-b+std::sqrt(d))*q;
		GLfloat t = t0<t1?t0:t1;

		return cam + ray*t;
	}
	return oglplus::Vec3f();
}
Exemple #9
0
math::Vector World::ShakeMagnitude() const
{
    double sx = shake_length_ - shake_;
    double amt;
    if (sx == 0.0) return math::Vector(0, 0);
    amt = shake_magnitude_ * sin(M_PI * 4 * sx * shake_frequency_) / (M_PI * 4 * sx * shake_frequency_);
    return Normalized(shake_direction_) * amt;
}
Exemple #10
0
void Camera::MoveLocalZ(float dist){
	Vector3 newEye = m_Eye;
	Vector3 newDst = m_Lookat;
	Vector3 direction = Normalized(m_View);
	direction *= dist;
	newEye += direction;
	newDst += direction;
	SetView(newEye, newDst, m_Up);
}
void calcBezier::calculeOrthogonal(Geometry::Point3D &p0, Geometry::Point3D &p1, Geometry::Point3D &p2, Geometry::Point3D &p3, Geometry::Point3D &p00, Geometry::Point3D &p11, Geometry::Point3D &p22, Geometry::Point3D &p33, double largeur)
{
    Geometry::Vector A(p1.getX()-p0.getX(), p1.getY()-p0.getY(), p1.getZ() - p0.getZ());
    Geometry::Vector B(p3.getX()-p2.getX(), p3.getY()-p2.getY(), p3.getZ() - p2.getZ());
    Geometry::Vector AO = Orthogonal(A);
    AO = Normalized(AO);
    AO = AO * largeur;
    Geometry::Vector BO = Orthogonal(B);
    BO = Normalized(BO);
    BO = BO * largeur;

    p00.setX(p0.getX()+AO[0]);
    p00.setY(p0.getY()+AO[1]);
    p00.setZ(p0.getZ()+AO[2]);

    if (p00.getZ() !=0 )
    {
        AO = Orthogonal(AO);
        AO = Normalized(AO);
        AO = AO * largeur;



        p00.setX(p0.getX()+AO[0]);
        p00.setY(p0.getY()+AO[1]);
        p00.setZ(p0.getZ()+AO[2]);
    }


    p11.setX(p1.getX()+AO[0]);
    p11.setY(p1.getY()+AO[1]);
    p11.setZ(p1.getZ()+AO[2]);


    p22.setX(p2.getX()+BO[0]);
    p22.setY(p2.getY()+BO[1]);
    p22.setZ(p2.getZ()+BO[2]);


    p33.setX(p3.getX()+BO[0]);
    p33.setY(p3.getY()+BO[1]);
    p33.setZ(p3.getZ()+BO[2]);

}
Exemple #12
0
void Camera::SetView(const Vector3& pvEye, const Vector3& pvLookat, const Vector3& pvUp){
	SetEye(pvEye);
	SetLookat(pvLookat);
	SetUp(pvUp);
	
	m_View = Normalized(m_Lookat - m_Eye);
	m_Cross = Cross(m_Up, m_View);

	m_matView = LookAt(m_Eye,m_Lookat, m_Up);
}
void calcBezier::calculeOrthogonalSimple(Geometry::Point3D &p0, Geometry::Point3D &p1, Geometry::Point3D &p00, double largeur)
{
    Geometry::Vector A(p1.getX()-p0.getX(), p1.getY()-p0.getY(), p1.getZ() - p0.getZ());
    Geometry::Vector AO = Orthogonal(A);
    AO = Normalized(AO);
    AO = AO * largeur;

    p00.setX(p0.getX()+AO[0]);
    p00.setY(p0.getY()+AO[1]);
    p00.setZ(p0.getZ()+AO[2]);
}
Exemple #14
0
oglplus::Vec3f SpectraDocumentView::PickOnPlane(unsigned vn, GLint x, GLint y)
{
	oglplus::Vec3f cam = camera_matrix.Position();
	oglplus::Vec3f ray = Normalized(ScreenToWorld(x, y) - cam);

	if(ray[vn] != 0)
	{
		GLfloat t = -cam[vn]/ray[vn];
		return cam + ray * t;
	}
	return ray * camera_distance;
}
Exemple #15
0
corgi::EntityRef PlayerComponent::SpawnProjectile(corgi::EntityRef source) {
  const SushiConfig* current_sushi = static_cast<const SushiConfig*>(
      entity_manager_->GetComponent<ServicesComponent>()
          ->world()
          ->SelectedSushi()
          ->data());
  corgi::EntityRef projectile =
      entity_manager_->GetComponent<ServicesComponent>()
          ->entity_factory()
          ->CreateEntityFromPrototype(current_sushi->prototype()->c_str(),
                                      entity_manager_);
  GraphComponent* graph_component =
      entity_manager_->GetComponent<GraphComponent>();
  graph_component->EntityPostLoadFixup(projectile);

  TransformData* transform_data = Data<TransformData>(projectile);
  PhysicsData* physics_data = Data<PhysicsData>(projectile);
  PlayerProjectileData* projectile_data =
      Data<PlayerProjectileData>(projectile);

  TransformComponent* transform_component = GetComponent<TransformComponent>();
  transform_data->position =
      transform_component->WorldPosition(source) +
      mathfu::kAxisZ3f * config_->projectile_height_offset();
  auto forward = CalculateProjectileDirection(source);
  auto velocity = current_sushi->speed() * forward +
                  current_sushi->upkick() * mathfu::kAxisZ3f;
  transform_data->position +=
      velocity.Normalized() * config_->projectile_forward_offset();

  // Include the raft's current velocity to the thrown sushi.
  auto raft_entity =
      entity_manager_->GetComponent<ServicesComponent>()->raft_entity();
  auto raft_rail = raft_entity ? Data<RailDenizenData>(raft_entity) : nullptr;
  if (raft_rail != nullptr) velocity += raft_rail->Velocity();

  physics_data->SetVelocity(velocity);
  physics_data->SetAngularVelocity(RandomProjectileAngularVelocity());
  auto physics_component = entity_manager_->GetComponent<PhysicsComponent>();
  physics_component->UpdatePhysicsFromTransform(projectile);

  projectile_data->owner = source;

  // TODO: Preferably, this should be a step in the entity creation.
  transform_component->UpdateChildLinks(projectile);

  Data<AttributesData>(source)->attributes[AttributeDef_ProjectilesFired]++;

  return corgi::EntityRef();
}
Exemple #16
0
static rmatrix MakeSaneWorldMatrix(const v3& pos, v3 dir, v3 up)
{
#ifdef _DEBUG
	if (!IS_EQ(MagnitudeSq(dir), 1))
	{
		DMLog("dir %f\n", Magnitude(dir));
		if (IS_EQ(MagnitudeSq(dir), 0))
			dir = { 1, 0, 0 };
		else
			Normalize(dir);
	}
	if (!IS_EQ(MagnitudeSq(up), 1))
	{
		DMLog("up %f\n", Magnitude(up));
		if (IS_EQ(MagnitudeSq(up), 0))
			up = { 1, 0, 0 };
		else
			Normalize(up);
	}
#endif

	auto mat = GetIdentityMatrix();

	auto right = Normalized(CrossProduct(dir, up));
	up = Normalized(CrossProduct(right, dir));
	v3 basis[] = {
		right,
		dir,
		up };
	for (size_t i{}; i < 3; ++i)
		for (size_t j{}; j < 3; ++j)
			mat.m[i][j] = basis[i][j];

	SetTransPos(mat, pos);

	return mat;
}
std::vector<double> Stationary(const DblCubeHypermatrix& P) {
  int dim = P.dim();
  std::vector<double> x(dim * dim, 1.0 / (dim * dim));
  int max_iter = 1000;
  double tol = 1e-12;
  for (int iter = 0; iter < max_iter; ++iter) {
    std::vector<double> x_next = Apply(P, x);
    // Check the difference
    double diff = L1Diff(x_next, x);
    x = x_next;
    x = Normalized(x);
    // Stop if difference is small enough
    if (diff < tol) { break; }
  }
  return x;
}
Exemple #18
0
Quaternion Quaternion::SetFromEulerAngles(Vector3 &ea) {
	float c1 = cosf(ea[2] * 0.5f);
	float c2 = cosf(ea[1] * 0.5f);
	float c3 = cosf(ea[0] * 0.5f);
	float s1 = sinf(ea[2] * 0.5f);
	float s2 = sinf(ea[1] * 0.5f);
	float s3 = sinf(ea[0] * 0.5f);

	m_x = c1*c2*s3 - s1*s2*c3;
	m_y = c1*s2*c3 + s1*c2*s3;
	m_z = s1*c2*c3 - c1*s2*s3;
	m_w = c1*c2*c3 + s1*s2*s3;

	(*this) = Normalized();

	return (*this);
}
Exemple #19
0
	GLuint Bitangents(std::vector<T>& dest) const
	{
		unsigned k = 0, n = _vertex_count();
		dest.resize(n * 3);
		Vec3f bitangent(Normalized(_v));

		for(unsigned i=0; i!=n; ++i)
		{
			dest[k++] = T(bitangent.x());
			dest[k++] = T(bitangent.y());
			dest[k++] = T(bitangent.z());
		}
		//
		assert(k == dest.size());
		// 3 values per vertex
		return 3;
	}
std::vector<double> SpaceyStationary(const DblCubeHypermatrix& P,
				     int max_iter=1000, double gamma=0.01,
				     double tol=1e-12) {
  int dim = P.dim();
  std::vector<double> x(dim, 1.0 / dim);
  for (int iter = 0; iter < max_iter; ++iter) {
    std::vector<double> x_next = HypermatrixApply(P, x);
    // Check the difference
    double diff = L1Diff(x_next, x);
    for (int j = 0; j < x.size(); ++j) {
      x[j] = (1.0 - gamma) * x_next[j] + gamma * x[j];
    }
    x = Normalized(x);
    // Stop if difference is small enough
    if (diff < tol) { break; }
  }
  return x;
}
    virtual double Intersect(const Ray &ray, Vector& normal, bool culling)
    {
        assert(mVertices.size() % 3 == 0);
        double distance = DBL_MAX;
        bool found = false;

        if (!m_BB.intersect(ray))
            return 0.0;

        for (size_t i = 0; i < mBoundingBoxes.size(); i++)
        {
            if (mBoundingBoxes[i].intersect(ray))
            {
                auto start = i*mDivCount;
                auto end = std::min(mVertices.size(), (size_t)i*mDivCount + mDivCount);

                for (size_t j = start; j < end; j += 3)
                {
                    auto d = 0.0;
                    auto V1 = mVertices[j];
                    auto V2 = mVertices[j + 1];
                    auto V3 = mVertices[j + 2];

                    auto edgeA = V2 - V1;
                    auto edgeB = V3 - V1;
                    auto edgeACrossEdgeB = edgeA.Cross(edgeB);

                    auto currentNormal = edgeACrossEdgeB.Normalized();

                    if (intersection(ray, d, V1, V2, V3, currentNormal, culling))
                    {
                        distance = std::min(distance, d);
                        normal = currentNormal;
                        found = true;
                    }
                }
            }
        }

        if (!found)
            return 0.0f;

        return distance;
    }
void Polygone::shrinkPoly(int nb, float l)
{
    if(l == 0)
        return;

    Vector2D tab[nb];

    for(int i = 0;	i < nb;	i++)
    {
        Vector2D v1 = Orthogonal(get(i)-get(i+1)).normalised();
        Vector2D v2 = Orthogonal(get(i-1)-get(i)).normalised();

        Vector2D v = (v1+v2)/2;	//quand on augmente de 1 sur v1 ou v2, on augment de v.norm() sur le v.

        tab[i] = get(i)+Normalized(v)*(l/v.getNorm());
    }
    for(int i = 0;	i < nb;	i++)
         set(i, tab[i]);
}
bool wxsItemRes::OnCanHandleFile(const wxString& FileName)
{
    wxFileName Normalized(GetProjectPath()+m_WxsFileName);
    Normalized.Normalize(wxPATH_NORM_DOTS);
    if ( Normalized.GetFullPath() == FileName )
    {
        return true;
    }
    if ( m_XrcFileName.empty() )
    {
        return false;
    }
    Normalized.Assign(GetProjectPath()+m_XrcFileName);
    Normalized.Normalize(wxPATH_NORM_DOTS);
    if ( Normalized.GetFullPath() == FileName )
    {
        return true;
    }
    return false;
}
Exemple #24
0
Color Light::Sample(Point const& point, Ray *out_ray, int row, int col) const {
  Point sample_point(0.0, 0.0, 0.0);
  double cell_width = 1.0 / cols;
  double cell_height = 1.0 / rows;

  if (area) {
    double offx = rand_double(-0.25, 0.25) / cols;
    double offy = rand_double(-0.25, 0.25) / rows;
    double x = (col * cell_width + offx) - 0.5;
    double y = (row * cell_height + offy) - 0.5;
    sample_point = Point(x, 0.0, y);
  }

  sample_point = transformation.Apply(sample_point);
  if (out_ray) {
    *out_ray = Ray(point, Normalized(sample_point - point));
    (*out_ray).max_dist = Norm(sample_point - point);
  }

  // In future, this may be deferred to a subclass for e.g. multicolored lights
  return color;
}
Exemple #25
0
// Return a "lookat" matrix44 given the current camera position (vector3),
//   camera-up vector3, and camera-target vector3.
matrix44 LookAtMatrix44(const vector3 &camPos, const vector3 &target, 
    const vector3 &camUp ) 
{
  matrix44 ret;

  vector3 F = target - camPos;
  F.normalize();

  vector3 S = CrossProduct(F, Normalized(camUp));
  S.normalize();

  vector3 U = CrossProduct(S, F);
  U.normalize();

  ret[0][0] = S.x;
  ret[1][0] = S.y;
  ret[2][0] = S.z;
  ret[3][0] = 0.0;

  ret[0][1] = U.x;
  ret[1][1] = U.y;
  ret[2][1] = U.z;
  ret[3][1] = 0.0;

  ret[0][2] = -F.x;
  ret[1][2] = -F.y;
  ret[2][2] = -F.z;
  ret[3][2] = 0.0;

  ret[0][3] = 0.0F;
  ret[1][3] = 0.0F;
  ret[2][3] = 0.0F;
  ret[3][3] = 1.0F;

  ret *= TranslateMatrix44(-camPos.x, -camPos.y, -camPos.z);

  return ret;
}
Exemple #26
0
	GLuint Normals(std::vector<T>& dest) const
	{
		dest.resize(20*3*3);

		const GLdouble* p = _positions();
		const GLushort* i = _indices();

		for(GLuint f=0; f!=20; ++f)
		{
			Vector<T, 3> v0(p+i[f*3+0]*3, 3);
			Vector<T, 3> v1(p+i[f*3+1]*3, 3);
			Vector<T, 3> v2(p+i[f*3+2]*3, 3);
			Vector<T, 3> fn(Normalized(Cross(v1-v0, v2-v0)));

			for(GLuint v=0; v!=3; ++v)
			{
				for(GLuint c=0; c!=3; ++c)
				{
					dest[f*9+v*3+c] = fn[c];
				}
			}
		}
		return 3;
	}
Exemple #27
0
	inline Vec3f Bitangential(void) const
	{
		return Normalized(_v);
	}
Exemple #28
0
	inline Vec3f Tangential(void) const
	{
		return Normalized(_u);
	}
Exemple #29
0
	inline Vec3f Normal(void) const
	{
		return Normalized(Cross(_u, _v));
	}
v3 GetNormal(const RCONVEXPOLYGONINFO *poly, const rvector &position,
	int au, int av)
{
	int nSelPolygon = -1, nSelEdge = -1;
	float fMinDist = FLT_MAX;

	if (poly->nVertices == 3)
		nSelPolygon = 0;
	else
	{
		rvector pnormal(poly->plane.a, poly->plane.b, poly->plane.c);

		for (int i = 0; i < poly->nVertices - 2; i++)
		{
			const auto& a = poly->pVertices[0];
			const auto& b = poly->pVertices[i + 1];
			const auto& c = poly->pVertices[i + 2];

			if (IntersectTriangle(a, b, c, position + pnormal, -pnormal, nullptr))
			{
				nSelPolygon = i;
				nSelEdge = -1;
				break;
			}
			else
			{
				float dist = GetDistance(position, a, b);
				if (dist < fMinDist) { fMinDist = dist; nSelPolygon = i; nSelEdge = 0; }
				dist = GetDistance(position, b, c);
				if (dist < fMinDist) { fMinDist = dist; nSelPolygon = i; nSelEdge = 1; }
				dist = GetDistance(position, c, a);
				if (dist < fMinDist) { fMinDist = dist; nSelPolygon = i; nSelEdge = 2; }
			}
		}
	}

	auto& v0 = poly->pVertices[0];
	auto& v1 = poly->pVertices[nSelPolygon + 1];
	auto& v2 = poly->pVertices[nSelPolygon + 2];

	auto& n0 = poly->pNormals[0];
	auto& n1 = poly->pNormals[nSelPolygon + 1];
	auto& n2 = poly->pNormals[nSelPolygon + 2];

	v3 pos;
	if (nSelEdge != -1)
	{
		auto& e0 = nSelEdge == 0 ? v0 : nSelEdge == 1 ? v1 : v2;
		auto& e1 = nSelEdge == 0 ? v1 : nSelEdge == 1 ? v2 : v0;

		auto dir = Normalized(e1 - e0);

		pos = e0 + DotProduct(dir, position - e0) * dir;
	}
	else
		pos = position;

	auto a = v1 - v0;
	auto b = v2 - v1;
	auto x = pos - v0;

	float f = b[au] * x[av] - b[av] * x[au];
	if (IS_ZERO(f))
		return n0;

	float t = (a[av] * x[au] - a[au] * x[av]) / f;

	auto tem = InterpolatedVector(n1, n2, t);

	auto inter = a + t*b;

	int axisfors;
	if (fabs(inter.x) > fabs(inter.y) && fabs(inter.x) > fabs(inter.z))
		axisfors = 0;
	else if (fabs(inter.y) > fabs(inter.z))
		axisfors = 1;
	else
		axisfors = 2;

	float s = x[axisfors] / inter[axisfors];
	return InterpolatedVector(n0, tem, s);
}