public void SetEffectMatrix(SCN0Node node, ModelPanelViewport v, float frame) { if (MapMode != MappingMethod.TexCoord) { switch (MapMode) { case MappingMethod.Projection: _effectMatrix = Matrix34.ProjectionMapping(SCN0RefCamera, node, v, frame); break; case MappingMethod.EnvSpec: _effectMatrix = Matrix34.EnvSpecMap(SCN0RefCamera, SCN0RefLight, node, v, frame); break; case MappingMethod.EnvLight: _effectMatrix = Matrix34.EnvLightMap(SCN0RefLight, node, v, frame); break; case MappingMethod.EnvCamera: _effectMatrix = Matrix34.EnvCamMap(SCN0RefCamera, node, v, frame); break; default: _effectMatrix = Matrix.Identity; break; } } else { _effectMatrix = Matrix.Identity; } }
public static Matrix ProjectionMapping( int ref_camera, SCN0Node node, ModelPanelViewport v, float frame) { Matrix projMtx = Matrix.Identity; Matrix camMtx = Matrix.Identity; GLCamera cam = v.Camera; if (ref_camera >= 0 && node?.CameraGroup != null && ref_camera < node.CameraGroup.Children.Count) { // Set so that the image is projected from the specified camera. // Transform to the viewing coordinate system of the specified camera SCN0CameraNode camNode = (SCN0CameraNode)node.CameraGroup.Children[ref_camera]; camNode.GetModelViewMatrix(frame, out Matrix cm, out Matrix cmInv); camMtx = cm * cam._matrix; projMtx = (Matrix)ProjectionTexMtx(camNode, frame); } else { camMtx = cam._matrix; projMtx = (Matrix)ProjectionTexMtx(cam); } return(projMtx * camMtx); }
public unsafe void RenderTransformControls(ModelPanelViewport panel) { if (_playing || ControlType == TransformType.None) { return; } bool hasBone = SelectedBone != null; if (hasBone || VertexLoc.HasValue) { Vector3 pos; Matrix rot = Matrix.Identity; float radius; if (hasBone) { pos = BoneLoc(SelectedBone); radius = OrbRadius(pos, panel.Camera); switch (_coordinateTypes[(int)ControlType]) { case CoordinateType.Local: rot = GetBoneWorldMtx().GetRotationMatrix(); break; case CoordinateType.World: //rot = Matrix.Identity; //Already set to identity above break; case CoordinateType.Screen: //rot = CameraFacingRotationMatrix(panel, pos); rot = Matrix.RotationMatrix(panel.Camera._rotation); break; } RenderTransformControl(pos, rot, radius, panel, _boneSelection); } if (VertexLoc.HasValue) { pos = VertexLoc.Value; radius = OrbRadius(pos, panel.Camera); switch (_coordinateTypes[(int)ControlType]) { case CoordinateType.Local: case CoordinateType.World: //rot = Matrix.Identity; //Already set to identity above break; case CoordinateType.Screen: rot = CameraFacingRotationMatrix(panel, pos); break; } RenderTransformControl(pos, rot, radius, panel, _vertexSelection); } } }
/// <summary> /// Use this for transforming points /// </summary> public bool GetTransformPoint( Vector2 mousePoint, out Vector3 point, ModelPanelViewport panel, Vector3 center, SelectionParams selection) { return(GetTransformPoint(mousePoint, out point, panel, Matrix.TranslationMatrix(center), selection)); }
private static unsafe Vector3 GetLightLook( SCN0Node node, int refLight, Matrix invCamMtx, ModelPanelViewport v, float frame, out bool specEnabled) { Vector3 start, end; LightType lightType; if (node?.LightGroup != null && refLight < node.LightGroup.Children.Count && refLight >= 0) { //SCN0 light exists SCN0LightNode lightNode = (SCN0LightNode)node.LightGroup.Children[refLight]; start = lightNode.GetStart(frame); end = lightNode.GetEnd(frame); lightType = lightNode.LightType; specEnabled = lightNode.SpecularEnabled; } else //Use the model viewer light settings by default { start = (Vector3)v._posLight; end = new Vector3(); lightType = LightType.Directional; specEnabled = true; } //Don't use if not enabled? //bool enabled = lightNode.GetEnabled(frame); Vector3 lgtLook = (end - start).Normalize(); bool temp = lgtLook._x == 0.0f && lgtLook._y == 0.0f && lgtLook._z == 0.0f; if (lightType != LightType.Spotlight && !specEnabled || temp) { // Use light position if they are diffuse light or if light has no direction. if (temp) { lgtLook = start; } lgtLook = -(invCamMtx.GetRotationMatrix() * lgtLook); if (lgtLook._x == 0.0f && lgtLook._y == 0.0f && lgtLook._z == 0.0f) { // If the light position is the origin, treat as if light is coming from the top of y-axis. lgtLook._y = -1.0f; } } else { lgtLook = invCamMtx.GetRotationMatrix() * lgtLook; } return(lgtLook); }
public unsafe void RenderOrb(IBoneNode bone, GLDisplayList list, ModelPanelViewport v, bool doScale) { float radius = MDL0BoneNode._nodeRadius * (doScale ? OrbRadius(bone, v) : 1.0f); Matrix m = Matrix.TransformMatrix(new Vector3(radius), new Vector3(), bone.Matrix.GetPoint()); GL.PushMatrix(); GL.MultMatrix((float *)&m); list.Call(); GL.PopMatrix(); }
public virtual unsafe void modelPanel1_PreRender(ModelPanelViewport vp) { if (vp != null) { if (vp._renderFloor) { OnRenderFloor(); } GL.Enable(EnableCap.DepthTest); GL.DepthFunc(DepthFunction.Lequal); } }
public static Matrix EnvCamMap(int refCam, SCN0Node node, ModelPanelViewport v, float frame) { GLCamera cam = v.Camera; if (refCam >= 0 && node?.CameraGroup != null && refCam < node.CameraGroup.Children.Count) { SCN0CameraNode camNode = (SCN0CameraNode)node.CameraGroup.Children[refCam]; camNode.GetModelViewMatrix(frame, out Matrix cm, out Matrix cmInv); return((Matrix)EnvironmentTexMtx() * cm.GetRotationMatrix()); } return((Matrix)EnvironmentTexMtx() * v.Camera._matrix.GetRotationMatrix()); }
private void MouseMoveTargetBone( ModelPanel panel, MouseEventArgs e, float depth, ModelPanelViewport v) { if (SelectedBone != null) { MouseMoveTarget( panel, e, depth, v, GetBoneWorldMtx(), GetBoneInvWorldMtx(), _boneSelection); } GetBone(panel, e, depth, v); }
private void MouseMoveTargetVertex( ModelPanel panel, MouseEventArgs e, float depth, ModelPanelViewport v) { if (VertexLoc.HasValue /* && v._renderAttrib._renderVertices*/) { Vector3 center = VertexLoc.Value; MouseMoveTarget( panel, e, depth, v, Matrix.TranslationMatrix(center), Matrix.TranslationMatrix(-center), _vertexSelection); } GetVertex(panel, e, depth, v); }
public unsafe void RenderTranslationControl( Vector3 position, float radius, Matrix rotation, ModelPanelViewport panel, SelectionParams selection) { Matrix m = Matrix.TransformMatrix(new Vector3(radius * 0.25f), new Vector3(), position) * CameraFacingRotationMatrix(panel, position); GL.PushMatrix(); GL.MultMatrix((float *)&m); GL.Color4(selection._hiCirc || selection._snapCirc ? Color.Yellow : Color.Gray); GL.Begin(BeginMode.LineLoop); GL.Vertex2(-0.5f, -0.5f); GL.Vertex2(-0.5f, 0.5f); GL.Vertex2(0.5f, 0.5f); GL.Vertex2(0.5f, -0.5f); GL.Vertex2(-0.5f, -0.5f); GL.End(); GL.PopMatrix(); //Enter local space m = Matrix.TransformMatrix(new Vector3(radius), new Vector3(), position) * rotation; GL.PushMatrix(); GL.MultMatrix((float *)&m); GetTranslationAxes(selection).Call(); GL.PopMatrix(); panel.SettingsScreenText["X"] = panel.Camera.Project(new Vector3(_axisLDist + 0.1f, 0, 0) * m) - new Vector3(8.0f, 8.0f, 0); panel.SettingsScreenText["Y"] = panel.Camera.Project(new Vector3(0, _axisLDist + 0.1f, 0) * m) - new Vector3(8.0f, 8.0f, 0); panel.SettingsScreenText["Z"] = panel.Camera.Project(new Vector3(0, 0, _axisLDist + 0.1f) * m) - new Vector3(8.0f, 8.0f, 0); }
protected virtual void modelPanel1_MouseDown(object sender, MouseEventArgs e) { _createdNewBone = false; ModelPanel panel = sender as ModelPanel; ModelPanelViewport viewport = panel.CurrentViewport; if (panel._draggingViewports) { return; } if (e.Button == MouseButtons.Left) { if (DoNotHighlightOnMouseMove) { HighlightStuff(e, panel); //Reset the cursor (HighlightStuff sets the cursor) panel.Cursor = Cursors.Default; } //Reset snap flags _boneSelection.ResetSnaps(); _vertexSelection.ResetSnaps(); MouseDownTargetBone(e, panel); if (!MouseDownTargetVertex(e, panel)) { if (CurrentFrame == 0 && TargetAnimType == NW4RAnimType.CHR && CHR0Editor.chkMoveBoneOnly.Checked && TargetModel is MDL0Node) { MDL0Node m = TargetModel as MDL0Node; m._dontUpdateMesh = true; } } //Ensure a redraw so the snapping indicators are correct panel.Invalidate(); } }
private bool CompareBoneDistanceRecursive(IBoneNode bone, Vector3 point, ref IBoneNode match, ModelPanelViewport v, bool doScale) { float dist = bone.Matrix.GetPoint().TrueDistance(point) / (doScale ? OrbRadius(bone, v) : 1.0f); if (Math.Abs(dist - MDL0BoneNode._nodeRadius) < 0.01f) { match = bone; return(true); } foreach (IBoneNode b in ((ResourceNode)bone).Children) { if (CompareBoneDistanceRecursive(b, point, ref match, v, doScale)) { return(true); } } return(false); }
public void RenderTransformControl( Vector3 pos, Matrix rot, float radius, ModelPanelViewport panel, SelectionParams selection) { switch (ControlType) { case TransformType.Translation: RenderTranslationControl(pos, radius, rot, panel, selection); break; case TransformType.Rotation: RenderRotationControl(pos, radius, rot, panel, selection); break; case TransformType.Scale: RenderScaleControl(pos, radius, rot, panel, selection); break; } }
public void RecalcFrameState(ModelPanelViewport v = null) { if (_overrideBone != null) { _frameMatrix = _overrideBone._frameMatrix; _inverseFrameMatrix = _overrideBone._inverseFrameMatrix; } else { if (_overrideLocalTranslate != new Vector3()) { _frameState = new FrameState(_frameState.Scale, _frameState.Rotate, _frameState.Translate + _overrideLocalTranslate); } if (_parent is MDL0BoneNode) { _frameMatrix = ((MDL0BoneNode)_parent)._frameMatrix * _frameState._transform; _inverseFrameMatrix = _frameState._iTransform * ((MDL0BoneNode)_parent)._inverseFrameMatrix; } else { _frameMatrix = _frameState._transform; _inverseFrameMatrix = _frameState._iTransform; } } if (BillboardSetting != BillboardFlags.Off && v != null && v.ApplyBillboardBones) { ApplyBillboard(v.Camera); } foreach (MDL0BoneNode bone in Children) { bone.RecalcFrameState(v); } }
public void HighlightStuff(MouseEventArgs e, ModelPanel panel) { panel.Capture(); _boneSelection.ResetHighlights(); _vertexSelection.ResetHighlights(); float depth = panel.GetDepth(e.X, e.Y); ModelPanelViewport v = panel.HighlightedViewport; foreach (MouseMoveTargetType targetFunc in _mouseMoveTargetType) { targetFunc(panel, e, depth, v); } #if DEBUG if (_renderDepth) { v.SettingsScreenText["Depth: " + depth.ToString()] = new Vector3(5.0f, v.Height - 20.0f, 0.5f); panel.Invalidate(); } #endif }
public virtual void PostRender(IModel model, ModelPanelViewport vp) { if (vp._renderAttrib._renderVertices) { model.RenderVertices(false, SelectedBone, vp.Camera); } if (vp._renderAttrib._renderNormals) { model.RenderNormals(); } if (vp._renderAttrib._renderBones) { model.RenderBones(vp); } model.RenderBoxes( vp._renderAttrib._renderModelBox, vp._renderAttrib._renderObjectBoxes, vp._renderAttrib._renderBoneBoxes, vp._renderAttrib._useBindStateBoxes); }
public void SetCamera(ModelPanelViewport v, float frame, bool retainAspect) { ViewportProjection proj = (ViewportProjection)(int)ProjectionType; if (v.ViewType != proj) { v.SetProjectionType(proj); } GLCamera cam = v.Camera; CameraAnimationFrame f = GetAnimFrame(frame); cam.Reset(); cam.Translate(f.Pos); Vector3 rotate = f.GetRotation(Type); cam.Rotate(rotate); float aspect = retainAspect ? cam.Aspect : f.Aspect; cam.SetProjectionParams(aspect, f.FovY, f.FarZ, f.NearZ); }
public unsafe void RenderScaleControl( Vector3 pos, float radius, Matrix rotation, ModelPanelViewport panel, SelectionParams selection) { //Enter local space Matrix m = Matrix.TransformMatrix(new Vector3(radius), new Vector3(), pos) * rotation; GL.PushMatrix(); GL.MultMatrix((float *)&m); GetScaleAxes(selection).Call(); GL.PopMatrix(); panel.SettingsScreenText["X"] = panel.Camera.Project(new Vector3(_axisLDist + 0.1f, 0, 0) * m) - new Vector3(8.0f, 8.0f, 0); panel.SettingsScreenText["Y"] = panel.Camera.Project(new Vector3(0, _axisLDist + 0.1f, 0) * m) - new Vector3(8.0f, 8.0f, 0); panel.SettingsScreenText["Z"] = panel.Camera.Project(new Vector3(0, 0, _axisLDist + 0.1f) * m) - new Vector3(8.0f, 8.0f, 0); }
public virtual unsafe void modelPanel1_PostRender(ModelPanelViewport vp) { GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); GL.Disable(EnableCap.Lighting); if (_targetModels != null) { foreach (IModel m in _targetModels) { PostRender(m, vp); } } GL.Disable(EnableCap.DepthTest); if (RenderLightDisplay /* && vp == ModelPanel.CurrentViewport*/) { OnRenderLightDisplay(vp); } if (TargetAnimType == NW4RAnimType.SCN && vp.RenderSCN0Controls) { RenderSCN0Controls(vp); } //For now we'll clear the depth buffer bit here. //We're not using the model depth in any way so it doesn't matter //The problem with not doing this at the moment is the rotation control clips with the model. //This is because the rotation axes need to be lequal depth tested against an invisible sphere //I don't know how to test a sub depth buffer and then always make it pass on the actual buffer GL.Clear(ClearBufferMask.DepthBufferBit); RenderTransformControls(vp); RenderDepth(vp); }
public unsafe override void modelPanel1_PostRender(ModelPanelViewport sender) { GL.Enable(EnableCap.Blend); GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha); GL.Disable(EnableCap.Lighting); GL.Disable(EnableCap.DepthTest); Attributes.PostRender(); //Render hurtboxes if (chkHurtboxes.Checked) { for (int i = 0; i < listPanel.lstHurtboxes.Items.Count; i++) { if (listPanel.lstHurtboxes.GetItemChecked(i)) { ((MiscHurtBox)listPanel.lstHurtboxes.Items[i]).Render(SelectedHurtbox != null && SelectedHurtbox.Index == i, Scriptor._hurtBoxType); } } } //Render hitboxes if (chkHitboxes.Checked && Manager.Moveset != null) { GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); GLDisplayList c = TKContext.GetRingList(); GLDisplayList s = TKContext.GetSphereList(); foreach (HitBox e in RunTime._hitBoxes) { e.Render(modelPanel.Camera.GetPoint()); } } base.modelPanel1_PostRender(sender); }
public void LinkZoom(ModelPanelViewport control, ModelPanelViewport affected) { control.Zoomed += affected.Zoom; }
public virtual void Render(ModelPanelViewport viewport) { }
public void LinkTranslate(ModelPanelViewport control, ModelPanelViewport affected) { control.Translated += affected.Translate; }
/// <summary> /// This function returns a texture matrix /// that will aim the texture to the midpoint between the active camera /// and the given reference camera or light. /// </summary> public static Matrix EnvSpecMap( int refCam, int refLight, SCN0Node node, ModelPanelViewport v, float frame) { // Normal environmental map when neither the light nor the camera is specified. Matrix34 finalMtx = EnvironmentTexMtx(); GLCamera cam = v.Camera; Vector3 vLook, camUp, camLook; Matrix camMtx = cam._matrixInverse; Matrix invCamMtx = cam._matrix; Matrix34 m34 = (Matrix34)camMtx; camLook._x = -m34[8]; camLook._y = -m34[9]; camLook._z = -m34[10]; if (refLight >= 0) { Vector3 lgtLook = GetLightLook(node, refLight, invCamMtx, v, frame, out bool specEnabled); // Specular light is already set as a vector taking the center position. if (!specEnabled) { vLook = GetHalfAngle(camLook, lgtLook); } else { vLook = -lgtLook; } if (Math.Abs(vLook._x) < 0.000001f && Math.Abs(vLook._z) < 0.000001f) { camUp._x = camUp._y = 0.0f; if (vLook._y <= 0.0f) { // Look straight down camUp._z = -1.0f; } else { // Look straight up camUp._z = 1.0f; } } else { camUp._x = camUp._z = 0.0f; camUp._y = 1.0f; } } else if (refCam >= 0) { SCN0CameraNode camNode = null; if (node?.CameraGroup != null && refCam < node.CameraGroup.Children.Count) { camNode = (SCN0CameraNode)node.CameraGroup.Children[refCam]; } else { camNode = new SCN0CameraNode(); } camNode.GetModelViewMatrix(frame, out Matrix cM, out Matrix cMInv); // Map from the midpoint of the view camera and the specified camera. Matrix34 lgtCam = (Matrix34)cM; camUp._x = lgtCam[4]; camUp._y = lgtCam[5]; camUp._z = lgtCam[6]; Vector3 lgtLook = new Vector3(-lgtCam[8], -lgtCam[9], -lgtCam[10]); vLook = GetHalfAngle(camLook, lgtLook); } else { return((Matrix)finalMtx); } vLook.Normalize(); Vector3 vRight, vUp; vUp = (vRight = vLook.Cross(camUp).Normalize()).Cross(vLook); m34 = new Matrix34( vRight._x, vRight._y, vRight._z, 0.0f, vUp._x, vUp._y, vUp._z, 0.0f, vLook._x, vLook._y, vLook._z, 0.0f); m34 = (Matrix34)((Matrix)m34 * invCamMtx); m34[3] = 0.0f; m34[7] = 0.0f; m34[11] = 0.0f; return((Matrix)(finalMtx * m34)); }
public static Matrix EnvLightMap(int refLight, SCN0Node node, ModelPanelViewport v, float frame) { Matrix m = Matrix.Identity; GLCamera cam = v.Camera; Matrix camMtx = cam._matrix; Matrix invCamMtx = cam._matrixInverse; Matrix34 envMtx = new Matrix34( 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, -0.5f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 1.0f); //If no light is referenced, use the BrawlCrate built-in light if (refLight < 0 || node?.LightGroup != null && refLight >= node.LightGroup.Children.Count) { refLight = 0; node = null; } // The light position and direction needs to be transformed with the camera's inverse matrix. Vector3 vLook, camUp, vRight, vUp; vLook = GetLightLook(node, refLight, invCamMtx, v, frame, out bool specEnabled).Normalize(); // Calculate without using a target because the margin of error for calculations must be taken into account when the light is far away. // Take the absolute value as a measure against transformation margin. if (Math.Abs(vLook._x) < 0.000001f && Math.Abs(vLook._z) < 0.000001f) { camUp._x = camUp._y = 0.0f; if (vLook._y <= 0.0f) { // Look straight down camUp._z = -1.0f; } else { // Look straight up camUp._z = 1.0f; } } else { camUp._x = camUp._z = 0.0f; camUp._y = 1.0f; } vUp = (vRight = vLook.Cross(camUp).Normalize()).Cross(vLook); Matrix34 m34 = new Matrix34( vRight._x, vRight._y, vRight._z, 0.0f, vUp._x, vUp._y, vUp._z, 0.0f, -vLook._x, -vLook._y, -vLook._z, 0.0f); m34 = (Matrix34)((Matrix)m34 * invCamMtx); m34[3] = 0.0f; m34[7] = 0.0f; m34[11] = 0.0f; return((Matrix)(envMtx * m34)); //return (Matrix)envMtx * Matrix.RotationMatrix(new Vector3().LookatAngles((Vector3)v._posLight) * Maths._rad2degf); }
public void LinkScale(ModelPanelViewport control, ModelPanelViewport affected) { control.Scaled += affected.Scale; }
private void modelPanel_OnCurrentViewportChanged(GLViewport p) { ModelPanelViewport v = p as ModelPanelViewport; stretchToolStripMenuItem1.Checked = p.BackgroundImageType == BGImageType.Stretch; centerToolStripMenuItem1.Checked = p.BackgroundImageType == BGImageType.Center; resizeToolStripMenuItem1.Checked = p.BackgroundImageType == BGImageType.ResizeWithBars; bool camDefaultSet = p.Camera._defaultRotate != p.GetDefaultRotate() || p.Camera._defaultScale != p.GetDefaultScale() || p.Camera._defaultTranslate != new Vector3(); btnSaveCam.Text = camDefaultSet ? "Clear Camera" : "Save Camera"; modelPanel_RenderBonesChanged(ModelPanel, v.RenderBones); modelPanel_RenderFloorChanged(ModelPanel, v.RenderFloor); modelPanel_RenderModelBoxChanged(ModelPanel, v.RenderModelBox); modelPanel_RenderNormalsChanged(ModelPanel, v.RenderNormals); modelPanel_RenderObjectBoxChanged(ModelPanel, v.RenderObjectBox); modelPanel_RenderOffscreenChanged(ModelPanel, v.DontRenderOffscreen); ModelPanel_RenderPolygonsChanged(ModelPanel, v.RenderPolygons); ModelPanel_RenderVerticesChanged(ModelPanel, v.RenderVertices); modelPanel_RenderVisBoneBoxChanged(ModelPanel, v.RenderVisBoneBox); ModelPanel_RenderWireframeChanged(ModelPanel, v.RenderWireframe); ModelPanel_ScaleBonesChanged(ModelPanel, v.ScaleBones); ModelPanel_ApplyBillboardBonesChanged(ModelPanel, v.ApplyBillboardBones); modelPanel_FirstPersonCameraChanged(ModelPanel, v._firstPersonCamera); ModelPanel_RenderShadersChanged(ModelPanel, v._renderAttrib._renderShaders); ModelPanel_UseBindStateBoxesChanged(ModelPanel, v.UseBindStateBoxes); sCN0ToolStripMenuItem.Checked = v.RenderSCN0Controls; _currentProjBox.Checked = false; switch (p.ViewType) { case ViewportProjection.Perspective: _currentProjBox = perspectiveToolStripMenuItem; break; case ViewportProjection.Orthographic: _currentProjBox = orthographicToolStripMenuItem; break; case ViewportProjection.Top: _currentProjBox = topToolStripMenuItem; break; case ViewportProjection.Bottom: _currentProjBox = bottomToolStripMenuItem; break; case ViewportProjection.Left: _currentProjBox = leftToolStripMenuItem; break; case ViewportProjection.Right: _currentProjBox = rightToolStripMenuItem; break; case ViewportProjection.Front: _currentProjBox = frontToolStripMenuItem; break; case ViewportProjection.Back: _currentProjBox = backToolStripMenuItem; break; } _currentProjBox.Checked = true; showCameraCoordinatesToolStripMenuItem.Checked = v._showCamCoords; loadImageToolStripMenuItem.Text = v.BackgroundImage == null ? "Load Image" : "Clear Image"; }
public void LinkPivot(ModelPanelViewport control, ModelPanelViewport affected) { control.Pivoted += affected.Pivot; }
public void LinkRotate(ModelPanelViewport control, ModelPanelViewport affected) { control.Rotated += affected.Rotate; }