/// Copyright (c) 2021 Iiro Iivanainen, Harri Linna, Jere Pakkanen, Riikka Vilavaara /// /// Permission is hereby granted, free of charge, to any person obtaining /// a copy of this software and associated documentation files (the /// "Software"), to deal in the Software without restriction, including /// without limitation the rights to use, copy, modify, merge, publish, /// distribute, sublicense, and/or sell copies of the Software, and to /// permit persons to whom the Software is furnished to do so, subject to /// the following conditions: /// /// The above copyright notice and this permission notice shall be included /// in all copies or substantial portions of the Software. /// /// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, /// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF /// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. /// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY /// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, /// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE /// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using System; using System.Numerics; using SharpGL; using SharpGL.WPF; using SharpGL.SceneGraph; using SharpGL.SceneGraph.Assets; namespace WPFOpenGL { public class Quad : Shape { private Vector3 v0; private Vector3 v1; private Vector3 v2; private Vector3 v3; private Texture texture = null; private Vector4 color = new Vector4(1f,1f,1f,1f); private Vector4 outlineColor = new Vector4(1f,1f,1f,1f); public bool drawOutline = true; private Vector3 eulerAngle = new Vector3(1f, 1f, 1f); private Vector3 centerPosition = new Vector3(0f, 0f, 0f); private Vector2 size = new Vector2(2f, 2f); /// v2 v3 /// +--------+ /// | | /// | | /// | | /// +--------+ /// v0 v1 public Quad(Vector3 centerPosition, Vector3 eulerAngle, Vector2 size) { this.eulerAngle = eulerAngle; this.centerPosition = centerPosition; this.size = size; calculateVertices(); } public void setQuad(Vector3 centerPosition, Vector3 eulerAngle, Vector2 size) { this.eulerAngle = eulerAngle; this.centerPosition = centerPosition; this.size = size; calculateVertices(); } public void setEulerAngle(Vector3 eulerAngle) { this.eulerAngle = eulerAngle; calculateVertices(); } public void setPosition(Vector3 centerPosition) { this.centerPosition = centerPosition; calculateVertices(); } public void setSize(Vector2 size) { this.size = size; calculateVertices(); } public void incerease(float depth) { Vector3 normal = Vector3.Normalize(Vector3.Cross(v1-v0, v2-v0)); centerPosition += depth * normal; calculateVertices(); } public void SetTexture(Texture texture) { this.texture = texture; } public void SetOutlineColor(Vector4 outlineColor) { this.outlineColor = outlineColor; } public void SetColor(Vector4 color) { this.color = new Vector4(color.X, color.Y, color.Z, color.W); } private void calculateVertices() { v0 = new Vector3(centerPosition.X - size.X / 2f, centerPosition.Y - size.Y / 2f, centerPosition.Z); v1 = new Vector3(centerPosition.X + size.X / 2f, centerPosition.Y - size.Y / 2f, centerPosition.Z); v2 = new Vector3(centerPosition.X - size.X / 2f, centerPosition.Y + size.Y / 2f, centerPosition.Z); v3 = new Vector3(centerPosition.X + size.X / 2f, centerPosition.Y + size.Y / 2f, centerPosition.Z); v0 = centerPosition + MathHelper.RotateX(v0 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.X)); v0 = centerPosition + MathHelper.RotateY(v0 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Y)); v0 = centerPosition + MathHelper.RotateZ(v0 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Z)); v1 = centerPosition + MathHelper.RotateX(v1 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.X)); v1 = centerPosition + MathHelper.RotateY(v1 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Y)); v1 = centerPosition + MathHelper.RotateZ(v1 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Z)); v2 = centerPosition + MathHelper.RotateX(v2 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.X)); v2 = centerPosition + MathHelper.RotateY(v2 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Y)); v2 = centerPosition + MathHelper.RotateZ(v2 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Z)); v3 = centerPosition + MathHelper.RotateX(v3 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.X)); v3 = centerPosition + MathHelper.RotateY(v3 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Y)); v3 = centerPosition + MathHelper.RotateZ(v3 - centerPosition, MathHelper.DegreesToRadians(eulerAngle.Z)); } public override void Render(OpenGL openGL) { calculateVertices(); if (!isVisible) return; if (texture != null) { openGL.Enable(OpenGL.GL_TEXTURE_2D); openGL.Enable(OpenGL.GL_BLEND); openGL.BlendFunc(OpenGL.GL_SRC_ALPHA, OpenGL.GL_ONE_MINUS_SRC_ALPHA); texture.Bind(openGL); openGL.Begin(OpenGL.GL_QUADS); openGL.Color(1f,1f,1f,1f); openGL.TexCoord(1.0f, 1.0f); openGL.Vertex(v3.X, v3.Y, v3.Z); openGL.TexCoord(1.0f, 0.0f); openGL.Vertex(v1.X, v1.Y, v1.Z); openGL.TexCoord(0.0f, 0.0f); openGL.Vertex(v0.X, v0.Y, v0.Z); openGL.TexCoord(0.0f, 1.0f); openGL.Vertex(v2.X, v2.Y, v2.Z); openGL.End(); openGL.Disable(OpenGL.GL_TEXTURE_2D); openGL.Disable(OpenGL.GL_BLEND); } else { openGL.Begin(OpenGL.GL_QUADS); openGL.Color(color.X,color.Y,color.Z,color.W); openGL.Vertex(v3.X, v3.Y, v3.Z); openGL.Vertex(v1.X, v1.Y, v1.Z); openGL.Vertex(v0.X, v0.Y, v0.Z); openGL.Vertex(v2.X, v2.Y, v2.Z); openGL.End(); } openGL.Begin(OpenGL.GL_LINE_LOOP); openGL.Color(outlineColor.X, outlineColor.Y, outlineColor.Z, outlineColor.W); openGL.Vertex(v0.X, v0.Y, v0.Z); openGL.Vertex(v1.X, v1.Y, v1.Z); openGL.Vertex(v3.X, v3.Y, v3.Z); openGL.Vertex(v2.X, v2.Y, v2.Z); openGL.Vertex(v0.X, v0.Y, v0.Z); openGL.End(); } public override bool IntersectRay(Vector3 rayOrigin, Vector3 rayDirection, out Vector3 hit, out float distance) { hit = new Vector3(0f, 0f, 0f); distance = float.MaxValue; if (!isVisible || !isRaycastable) return false; return RayCast.IntersectQuad(rayOrigin, rayDirection, v0, v1, v2, v3, out hit, out distance); } public Vector3 ClosestQuadPoint(Vector3 outsidePosition, MainWindow.Planes currentPlane) { Vector3 insidePosition = outsidePosition; if (currentPlane == MainWindow.Planes.XY) { // quad lies on XY plane if (outsidePosition.X < v0.X) insidePosition.X = v0.X; if (outsidePosition.X > v1.X) insidePosition.X = v1.X; if (outsidePosition.Y < v1.Y) insidePosition.Y = v1.Y; if (outsidePosition.Y > v2.Y) insidePosition.Y = v2.Y; } if (currentPlane == MainWindow.Planes.XZ) { // quad lies on XZ plane if (outsidePosition.X < v0.X) insidePosition.X = v0.X; if (outsidePosition.X > v1.X) insidePosition.X = v1.X; if (outsidePosition.Z < v1.Z) insidePosition.Z = v1.Z; if (outsidePosition.Z > v2.Z) insidePosition.Z = v2.Z; } if (currentPlane == MainWindow.Planes.YZ) { // quad lies on YZ plane !! note inverted Z coordinate if (outsidePosition.Z > v0.Z) insidePosition.Z = v0.Z; if (outsidePosition.Z < v1.Z) insidePosition.Z = v1.Z; if (outsidePosition.Y < v1.Y) insidePosition.Y = v1.Y; if (outsidePosition.Y > v2.Y) insidePosition.Y = v2.Y; } return insidePosition; } } }