3D小算法

向量投影

/// <summary>
/// 计算空间向量在平面上的投影
/// </summary>
/// <param name="o">原点</param>
/// <param name="end">终点</param>
/// <param name="unit">投影平面的法向量</param>
/// <returns></returns>
static Vector3 CreateProjection(Vector3 o, Vector3 end, Vector3 unit)
{
    unit = unit.Normalized();
    Vector3 v = end-o;
    var dotv = Vector3.Dot(v, unit);
    var b = dotv * unit;
    var retv = (v - b).Normalized();

    return retv;
}

两个向量角度

static double GetTowNormalAngle(Vector3 v1, Vector3 v2)
{
    v1.Normalize();
    v2.Normalize();
    var angle = v1.AngleBetween(v2) * 180 / Math.PI;    
    return angle;
}

两个向量投影角度

static double GetAngle(Vector3 v, Vector3 unitV, Vector3 unitN)
{
    v.Normalize();
    unitV.Normalize();
    unitN.Normalize();
    var dotv = Vector3.Dot(v, unitN);
    var b = Vector3D.Multiply(dotv, unitN.ToVector3D()).ToVector3();
    var retV = v - b;
    var angle = retV.AngleBetween(unitV) * 180 / Math.PI;
    return angle;
}

模型旋转移动

private void LeftMouse3DDown(object sender, RoutedEventArgs e)
{
    if (e is Mouse3DEventArgs arg)
    {
        viewport = arg.Viewport;
        Camera = viewport.Camera;
        startPoint = arg.Position;//roate
        startHitPoint = arg.HitTestResult.PointHit;//move
        isCaptured = true;
        arg.Handled = true;
        var model = sender as MeshGeometryModel3D;
        if (model != null && model.Material is DiffuseMaterial)
        {
            model.Material = DiffuseMaterials.Yellow;
        }
    }
}

private void LeftMouse3DUp(object sender, RoutedEventArgs e)
{
    var model = sender as MeshGeometryModel3D;
    if (isCaptured && e is Mouse3DEventArgs arg && model != null && model.Material is DiffuseMaterial)
    {
        model.Material = DiffuseMaterials.Red;
        arg.Handled = true;
    }
    isCaptured = false;
    viewport = null;
}


private void LeftMouse3DMove(object sender, RoutedEventArgs e)
{
    if (isCaptured && e is Mouse3DEventArgs arg && arg.Viewport == viewport)
    {
        var mouse = arg.OriginalInputEventArgs as MouseEventArgs;
        if (mouse == null)
            return;
        if (mouse.LeftButton == MouseButtonState.Pressed )
        {
            var newHit = viewport.UnProjectOnPlane(arg.Position, startHitPoint.ToPoint3D(), Camera.LookDirection);
            if (newHit.HasValue)
            {
                var newPos = newHit.Value.ToVector3();
                //newPos = new Vector3(newPos.X, startHitPoint.Y, newPos.Z); // trying to constraint elevation
                var offset = newPos - startHitPoint;
                startHitPoint = newPos;
                currentTranslation.TranslationVector += offset;
                UpdateTransform();
            }
        }
        else
        {
            RotateTrackball(startPoint, arg.Position, currentTranslation.TranslationVector);
            startPoint = arg.Position;
        }
        arg.Handled = true;
    }
}

private static Vector3 ProjectToTrackball(System.Windows.Point point, double w, double h)
{
    // Use the diagonal for scaling, making sure that the whole client area is inside the trackball
    double r = Math.Sqrt((w * w) + (h * h)) / 2;
    double x = (point.X - (w / 2)) / r;
    double y = ((h / 2) - point.Y) / r;
    double z2 = 1 - (x * x) - (y * y);
    double z = z2 > 0 ? Math.Sqrt(z2) : 0;

    return new Vector3((float)x, (float)y, (float)z);
}

private void RotateTrackball(System.Windows.Point p1, System.Windows.Point p2, Vector3 rotateAround)
{
    Vector3 v1, v2;
    if (ConstrainAxis.HasValue)
    {
        // preparing to compute the ratio to the screen
        var diag = Math.Sqrt(
            viewport.ActualWidth * viewport.ActualWidth +
            viewport.ActualHeight * viewport.ActualHeight
            );

        // can we project the constraintAxis onto the view?
        var t3 = Vector3.TransformCoordinate(ConstrainAxis.Value, Camera.CameraInternal.GetViewMatrix());
        var dir = new Vector2(t3.X, t3.Y); // axis of Constraint in view coordinates

        var pp1 = p1.ToVector2(); // computing distance perpendicular to axis in view coordinates
        var r1 = pp1 - (pp1 * dir);

        var pp2 = p2.ToVector2(); // computing distance perpendicular to axis in view coordinates
        var r2 = pp2 - (pp2 * dir);

        var angle = (r2.Length() - r1.Length()) / diag * 4;

        currentRotation *= Matrix.RotationAxis(Vector3.Normalize(ConstrainAxis.Value), (float)(angle * this.RotationSensitivity * 5));
        UpdateTransform();
    }
    else
    {
        v1 = ProjectToTrackball(p1, viewport.ActualWidth, viewport.ActualHeight);
        v2 = ProjectToTrackball(p2, viewport.ActualWidth, viewport.ActualHeight);

        // transform the trackball coordinates to view space
        var viewZ = Camera.CameraInternal.LookDirection;
        var viewX = Vector3.Cross(Camera.CameraInternal.UpDirection, viewZ);
        var viewY = Vector3.Cross(viewX, viewZ);
        viewX.Normalize();
        viewY.Normalize();
        viewZ.Normalize();
        var u1 = (viewZ * v1.Z) + (viewX * v1.X) + (viewY * v1.Y);
        var u2 = (viewZ * v2.Z) + (viewX * v2.X) + (viewY * v2.Y);

        var axis = Vector3.Cross(u1, u2);
        if (axis.LengthSquared() < 1e-8)
        {
            return;
        }

        var angle = u1.AngleBetween(u2);

        currentRotation *= Matrix.RotationAxis(Vector3.Normalize(axis), (float)(angle * this.RotationSensitivity * 5));
        UpdateTransform();
    }

}

private void UpdateTransform()
{
    totalTransform = currentRotation * currentTranslation;
    foreach (var item in GroupCameraModel)
    {
        item.Transform = CameraModelTransform = new MatrixTransform3D(totalTransform.ToMatrix3D());
    }
    UpdateLight();
    UpdateCameraData();
}
posted @ 2024-02-22 14:28  披着披风吹着风  阅读(36)  评论(0)    收藏  举报