I have a class called Cube in which I render a basic cube to the screen. I've got it working as far as rendering a basic cube of any scale at origin 0, 0, 0. However, if I want to move the cube, scale any of its axes, or rotate the cube; I have no idea where to begin since I have to achieve this within a Vertex object.
struct Vertex {
Vector3 Position;
Vector3 Normal;
Vector2 TextureUV;
Color Color;
}
To render a cube of size 1000000 I simply fill the Position properties with that value (with regards to negative and positive values to generate the faces).
new Vertex(new Vector3(-1000000, -1000000, -1000000), new Vector3(-1, -1, -1), Color.White);
Cube has four properties:
Position (Vector3).
Scale (Vector3).
Rotation (Vector3).
Color (SharpDX.Color).
My question is, as I am specifying Vertex locations, how can I calculate the new position when rotation and position are applied?
I attempted basic subtraction and addition which simply scales the cube differently and the origin remains at 0, 0, 0 (please don't mind the shorthand).
new Vertex(new Vector3(P.X - S.X, P.Y - S.Y, P.Z - S.Z), new Vector3(1, 1, 1), Color.White);
The above isn't the exact code as it is on a different computer currently.
If you need additional details, please let me know and I will clarify.
The Question
How can I set the position of the vertices based on the supplied position, scale, and rotation vectors?
Test Case
The following test cases are for position only and are to show you how I will test the answers given.
// Renders at origin.
Cube myCube = new Cube(new Vector3(0, 0, 0), new Vector3(100, 100, 100), Color.White);
// Renders to the left.
Cube myCubeLeft = new Cube(new Vector3(-250, 0, 0), new Vector3(100, 100, 100), Color.White);
// Renders to the right.
Cube myCubeRight = new Cube(new Vector3(250, 0, 0), new Vector3(100, 100, 100), Color.White);
// Renders above.
Cube myCubeUp = new Cube(new Vector3(0, -250, 0), new Vector3(100, 100, 100), Color.White);
// Renders below.
Cube myCubeDown = new Cube(new Vector3(0, 250, 0), new Vector3(100, 100, 100), Color.White);
I will add rotation and scaling test cases later if needed.
Related
I drew a line parallel to the Z axis in sharpgl. I set the camera above the Z axis, so I thought I would see 1 point, but I don't see it. Later I want to draw a large number of these lines side by side, now I'm trying on one.
Camera settings:
gl.Ortho (0, 1595, 0, 1000, -30, 80);
gl.LookAt(0.0f, 0.0f, 90.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);
Line Drawing:
gl.LineWidth (100.0f);
gl.Begin (OpenGL.GL_LINES);
gl.Color (0, 0, 0);
gl.Vertex (600, 600, 30);
gl.Vertex (600, 600, 50);
gl.End ();
gl.Flush ();
When I draw a line at a different angle, you can see it:
gl.Vertex (180, 15, 20);
gl.Vertex (800, 800, 20);
When I draw only a point, I also see it:
gl.PointSize (100.0f);
gl.Begin (OpenGL.GL_POINTS);
gl.Color (0, 0, 0);
gl.Vertex (800, 800, 20);
gl.End ();
I need to see the line. Do I need to add a point to the beginning and end of a line, or is it just something to set the line to be visible?
I want to check in Unity if the device has been rotated on all of it's axis.
So, I am reading the rotation of all the axis.
What should I do in order to validate for example that the user has "flipped" his device over the X-axis? I need to check the value, and see that they contain 0, 90, 180 and 270 degrees in a loop.
Here is part of my code:
void Update () {
float X = Input.acceleration.x;
float Y = Input.acceleration.y;
float Z = Input.acceleration.z;
xText.text = ((Mathf.Atan2(Y, Z) * 180 / Mathf.PI)+180).ToString();
yText.text = ((Mathf.Atan2(X, Z) * 180 / Mathf.PI)+180).ToString();
zText.text = ((Mathf.Atan2(X, Y) * 180 / Mathf.PI)+180).ToString();
}
The accelerometer only tells you if the acceleration of the device changes. So you will have values if the device started moving, or stopped moving. You can't retrieve its orientation from that.
Instead you need to use the gyroscope of the device. Most device have one nowadays.
Fortunately, Unity supports the gyroscope through the Gyroscope class
Simply using
Input.gyro.attitude
Will give you the orientation of the device in space, in the form of a quaternion.
To check the angles, use the eulerAngles function, for instance, is the device flipped in the x axis:
Vector3 angles = Input.gyro.attitude.eulerAngles;
bool xFlipped = angles.x > 180;
Be careful, you might have to invert some values if you want to apply the rotation in Unity (because it depend which orientation the devices uses for positive values, left or right)
// The Gyroscope is right-handed. Unity is left handed.
// Make the necessary change to the camera.
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
Here is the full example from the doc (Unity version 2017.3), in case the link above is broken. It shows how to read value from the gyroscope, and apply them to an object in Unity.
// Create a cube with camera vector names on the faces.
// Allow the device to show named faces as it is oriented.
using UnityEngine;
public class ExampleScript : MonoBehaviour
{
// Faces for 6 sides of the cube
private GameObject[] quads = new GameObject[6];
// Textures for each quad, should be +X, +Y etc
// with appropriate colors, red, green, blue, etc
public Texture[] labels;
void Start()
{
// make camera solid colour and based at the origin
GetComponent<Camera>().backgroundColor = new Color(49.0f / 255.0f, 77.0f / 255.0f, 121.0f / 255.0f);
GetComponent<Camera>().transform.position = new Vector3(0, 0, 0);
GetComponent<Camera>().clearFlags = CameraClearFlags.SolidColor;
// create the six quads forming the sides of a cube
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
quads[0] = createQuad(quad, new Vector3(1, 0, 0), new Vector3(0, 90, 0), "plus x",
new Color(0.90f, 0.10f, 0.10f, 1), labels[0]);
quads[1] = createQuad(quad, new Vector3(0, 1, 0), new Vector3(-90, 0, 0), "plus y",
new Color(0.10f, 0.90f, 0.10f, 1), labels[1]);
quads[2] = createQuad(quad, new Vector3(0, 0, 1), new Vector3(0, 0, 0), "plus z",
new Color(0.10f, 0.10f, 0.90f, 1), labels[2]);
quads[3] = createQuad(quad, new Vector3(-1, 0, 0), new Vector3(0, -90, 0), "neg x",
new Color(0.90f, 0.50f, 0.50f, 1), labels[3]);
quads[4] = createQuad(quad, new Vector3(0, -1, 0), new Vector3(90, 0, 0), "neg y",
new Color(0.50f, 0.90f, 0.50f, 1), labels[4]);
quads[5] = createQuad(quad, new Vector3(0, 0, -1), new Vector3(0, 180, 0), "neg z",
new Color(0.50f, 0.50f, 0.90f, 1), labels[5]);
GameObject.Destroy(quad);
}
// make a quad for one side of the cube
GameObject createQuad(GameObject quad, Vector3 pos, Vector3 rot, string name, Color col, Texture t)
{
Quaternion quat = Quaternion.Euler(rot);
GameObject GO = Instantiate(quad, pos, quat);
GO.name = name;
GO.GetComponent<Renderer>().material.color = col;
GO.GetComponent<Renderer>().material.mainTexture = t;
GO.transform.localScale += new Vector3(0.25f, 0.25f, 0.25f);
return GO;
}
protected void Update()
{
GyroModifyCamera();
}
protected void OnGUI()
{
GUI.skin.label.fontSize = Screen.width / 40;
GUILayout.Label("Orientation: " + Screen.orientation);
GUILayout.Label("input.gyro.attitude: " + Input.gyro.attitude);
GUILayout.Label("iphone width/font: " + Screen.width + " : " + GUI.skin.label.fontSize);
}
/********************************************/
// The Gyroscope is right-handed. Unity is left handed.
// Make the necessary change to the camera.
void GyroModifyCamera()
{
transform.rotation = GyroToUnity(Input.gyro.attitude);
}
private static Quaternion GyroToUnity(Quaternion q)
{
return new Quaternion(q.x, q.y, -q.z, -q.w);
}
}
I have written a script for a procedural cube. Basically I want to make a building whose width, length and height I can control through parameters. So by changing parameters I can make different shapes like windows and door and roof and many more. But I don't have any idea how to do that. How to reuse this script for different shapes?
using UnityEngine;
public class genralWindows: MonoBehaviour
{
public float height = 0.8 f;
public float width = 0.6 f;
public float length = 0;
void Start()
{
cube();
gameObject.GetComponent < Renderer > ().material.color = Color.white;
}
public void cube()
{
MeshFilter mf1 = GetComponent < MeshFilter > ();
Mesh mesh1 = mf1.mesh;
//vertices
Vector3[] vertices = new Vector3[]
{
//front face
new Vector3(-width, height, length), //left top front, 0
new Vector3(width, height, length), //right top front, height
new Vector3(-width, -height, length), //left bottom front, 2
new Vector3(width, -height, length), //right bottom front, width
//BACK FACE
new Vector3(width, height, -length), //right top back, 4
new Vector3(-width, height, -length), //left top back, length
new Vector3(width, -height, -length), //right bottom back, 6
new Vector3(-width, -height, -length), //left bottom back, 7
//LEFT FACE
new Vector3(-width, height, -length), //left top back,width
new Vector3(-width, height, length), //left top front,9
new Vector3(-width, -height, -length), //left bottom back,height0,
new Vector3(-width, -height, length), //left bottom front,heightheight
//RIGHT FACE
new Vector3(width, height, length), //right top front height2
new Vector3(width, height, -length), //right top back heightwidth
new Vector3(width, -height, length), //right bottom front height4
new Vector3(width, -height, -length), //right bottom back heightheight
//TOP FACE
new Vector3(-width, height, -length), //left top back height6
new Vector3(width, height, -length), //right top back height7
new Vector3(-width, height, length), //left top front heightwidth
new Vector3(width, height, length), //right top front height9
//BOTTOM FACE
new Vector3(-width, -height, length), //left bottom front 20
new Vector3(width, -height, length), //right bottom front 2height
new Vector3(-width, -height, -length), //left bottom back 22
new Vector3(width, -height, -length), //right bottom back 2width
};
//triangles// 3 points clockwise determines which side is visible
int[] triangles = new int[]
{
//front face
0,
2,
3, //first triangle
3,
1,
0, //second triangle
//back face
4,
6,
7, //first triangle
7,
5,
4, //second triangle
//left face
8,
10,
11, //first triangle
11,
9,
8, //second triangle
//right face
12,
14,
15, //first triangle
15,
13,
12, //second triangle
//top face
16,
18,
19, //first triangle
19,
17,
16, //second triangle
//bottom face
20,
22,
23, //first triangle
23,
21,
20 //second triangle
};
//UVs
Vector2[] uvs1 = new Vector2[]
{
//front face// 0,0 is bottom left, 1,1 is top right
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0),
new Vector2(0, 1),
new Vector2(0, 0),
new Vector2(1, 1),
new Vector2(1, 0)
};
mesh1.Clear();
mesh1.vertices = vertices;
mesh1.triangles = triangles;
mesh1.uv = uvs1;
mesh1.RecalculateNormals();
}
// Update is called once per frame
void Update()
{
cube();
}
}
You need to use inheritance.
Make a class Cube like this:
public class Cube:MonoBehaviour{
}
Then make other classes like this:
public class Door:Cube{
}
You can get more details here: https://unity3d.com/learn/tutorials/topics/scripting/inheritance
You don't really need to program the dimensions of a cube or other elements in your scene from scratch. Unity already offers some primitives you can instantiate in your game, and access their attributes from your script to modify their values.
In case you want to create an Enviroment programatically using GameObjects with primitives ( like cubes, spheres...) or custom GameObjects (like 3D models you created on Blender) you can do something like this:
//You will need to pass in the inspector a GameObject as parameter in the correct field
public GameObject customGameObject;
void Start()
{
generateEnviroment()
}
void generateEnviroment()
{
//You instantiate a GameObject of type cube
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
//You change its position
cube.transform.position = new Vector3(0, 0.5F, 0);
// Widen the object by 0.1
cube.transform.localScale += new Vector3(0.1F, 0, 0);
//Change material properties, assuming it has a material component
Renderer rend = cube.GetComponent<Renderer>();
rend.material.shader = Shader.Find("Specular");
rend.material.SetColor("_SpecColor", Color.red);
//In case you want to add other type of GameObject, like a car or sth you have created:
GameObject myGameObject = GameObject.Instantiate (customGameObject);
}
You can make that function as long as complex as you want, just being tidy with names.
https://docs.unity3d.com/ScriptReference/GameObject.CreatePrimitive.html
I'm trying to make a cooldown visual for my game when actions are used. I want the button that I have that is the action to be filled with a grayish semi-transparent color that "unwinds" clockwise (if that makes sense). Games like World of Warcraft do this where the time it takes for the cooldown is the time the angle of the unwinding takes. You can see an example here. In this picture the cooldown is more than 1/2 way finished.
http://www.vbforums.com/attachment.php?attachmentid=101705&stc=1&d=1372575930
I'm playing around with arc drawing but this doesn't give me what I'm after.
if (globalCD)
{
Pen p = new Pen(Color.FromArgb(125, 255, 0, 0), 10);
//e.Graphics.DrawLine(p, new Point(0, 0), new Point(10, 10));
e.Graphics.DrawArc(p, new Rectangle(0, 0, 64, 64), 270, 270);
}
In general, the Graphics class has a FillX method for each DrawX method. In this case the result is sufficiently different that the names actually change a bit, but you likely want the FillPie method.
if (globalCD)
{
Pen p = new Pen(Color.FromArgb(125, 255, 0, 0), 10);
//e.Graphics.DrawLine(p, new Point(0, 0), new Point(10, 10));
e.Graphics.FillPie(p, new Rectangle(0, 0, 64, 64), 270, 270);
}
How do you draw shapes, such as Rectangles and Circles, in MonoGame without having to save the a predrawn shape in the Content folder?
DrawRectangle() and DrawEllipse() are for Windows Form and do not work in OpenGL, which is what I am using.
Use 3D primitives and a 2D projection
Here's a simple example with explanations
I define a 10x10 rectangle and set the world matrix to make it look like a 2D projection :
Note : the BasicEffect is what draws your primitive
protected override void LoadContent()
{
_vertexPositionColors = new[]
{
new VertexPositionColor(new Vector3(0, 0, 1), Color.White),
new VertexPositionColor(new Vector3(10, 0, 1), Color.White),
new VertexPositionColor(new Vector3(10, 10, 1), Color.White),
new VertexPositionColor(new Vector3(0, 10, 1), Color.White)
};
_basicEffect = new BasicEffect(GraphicsDevice);
_basicEffect.World = Matrix.CreateOrthographicOffCenter(
0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height, 0, 0, 1);
}
Then I draw the whole thing :D
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
EffectTechnique effectTechnique = _basicEffect.Techniques[0];
EffectPassCollection effectPassCollection = effectTechnique.Passes;
foreach (EffectPass pass in effectPassCollection)
{
pass.Apply();
GraphicsDevice.DrawUserPrimitives(PrimitiveType.LineStrip, _vertexPositionColors, 0, 4);
}
base.Draw(gameTime);
}
There you have your rectangle !
Now this is just the tip the of the iceberg,
To draw filled rectangles : draw 2 triangle primitives
For an ellipse/circle see these : Draw an Ellipse in XNA and Draw simple circle in XNA
Or as mentioned in one of the posts above you could use a shader that does it instead ...
I needed to draw a Superellipse a while ago and ended up sketching this shader :
Drawing a SuperEllipse in HLSL
As you can see in the post a Superellipse not only draws ellipse but also other shapes and maybe even circles (I did not test) so you might be interested in it.
Ultimately you will want some class/methods to hide all these details so you just have to invoke something like DrawCircle().
Tip : by posting # https://gamedev.stackexchange.com/ you will likely get more answers for Monogame-related questions
:D
If you need to create a rectangle in 2D you can just do this:
Color[] data = new Color[rectangle.Width * rectangle.Height];
Texture2D rectTexture = new Texture2D(GraphicsDevice, rectangle.Width, rectangle.Height);
for (int i = 0; i < data.Length; ++i)
data[i] = Color.White;
rectTexture.SetData(data);
var position = new Vector2(rectangle.Left, rectangle.Top);
spriteBatch.Draw(rectTexture, position, Color.White);
Might be a tad bit easier than Aybe's answer in some situations. This creates a solid rectangle.
I found a simple solution for drawing filled and non-filled shapes, I don't know if it's power consuming or not, but here it is anyway:
{
//Filled
Texture2D _texture;
_texture = new Texture2D(graphicsDevice, 1, 1);
_texture.SetData(new Color[] { Color.White });
spriteBatch.Draw(_texture, Rect, Color.White);
}
{
//Non filled
Texture2D _texture;
_texture = new Texture2D(graphicsDevice, 1, 1);
_texture.SetData(new Color[] { Color.White });
spriteBatch.Draw(_texture, new Rectangle(Rect.Left, Rect.Top, Rect.Width, 1), Color.White);
spriteBatch.Draw(_texture, new Rectangle(Rect.Right, Rect.Top, 1, Rect.Height), Color.White);
spriteBatch.Draw(_texture, new Rectangle(Rect.Left, Rect.Bottom, Rect.Width, 1), Color.White);
spriteBatch.Draw(_texture, new Rectangle(Rect.Left, Rect.Top, 1, Rect.Height), Color.White);
}