I recently made a simple lua class (this is using the love2D engine) that made a hexagon by plotting 6 vertices and tracing them with lines, one of the things I wanted to be able to expand to was a hexagon grid but I didn't know how
All of the websites I found didn't help and I couldn't understand why.
Here's the function where I plot the vertices based on it's radius and x and y position.
My hexagons are in a pointy-topped style.
function hexagon.new(x,y,radius)
local hexagon=setmetatable({},hexagon)
hexagon.Vertices={}
hexagon.x=x or 0
hexagon.radius = radius or 10
hexagon.y=y or 0
for i=0,6 do
local angle = 2 * math.pi / 6 * (i + .5) -- 1 is what is multipled to 90(2*math.pi) so 1*90=90(flat-topped), 0.5*90=45(pointy-topped)
local x = hexagon.x + hexagon.radius * math.cos(angle)
local y = hexagon.y + hexagon.radius * math.sin(angle)
hexagon.Vertices[i]= {x=x,y=y}
end
return hexagon
end
Hope i understood well.
Here is working example of how to create hex and then draw it to grid with as simplified functions as possible, using Love2d engine (v. 0.10.2).
important: made for hex which has all six points on circle, using radius.
this approach is not suitable for all hex shapes.
------------------------------
--lets create hex data with center at position 0,0
--using only simple functions
--table of 6 points
hex = {};
--radius for hex which has all 6 points lying on circle
hex.radius = 10;
--simple loop to generate hex
local i = 1
repeat
--full circle has 2 radians and hex has 6 points
--so 1/3 pi increase per point
local direction = math.pi/3 * (i+0.5)
--rotate hex by 90 degrees: direction = math.pi/3 * i
--generate empty table to insert point coordinations to
hex[i] = {};
--set x,y coordinates
hex[i].x = hex.radius*math.cos( direction )
hex[i].y = hex.radius*math.sin( direction )
i = i+1
until i > 6
------------------------------
------------------------------
function love.draw( dt )
--get distance between 2 hex tiles using trigonometry
local jxOffset = hex.radius*-math.tan( math.pi/1.5 ) --or math.sqrt(3) * hex.radius
--offset new lines by this value
local ixOffset = jxOffset/4
--use direction and distance to get "y" offset between lines
--we got distance in jxOffset, so we just apply direction
local iyOffset = jxOffset*math.sin( math.pi/3 )
--"i" = line, "j" = tile in line
local i = 1
repeat
local j = 1
repeat
love.graphics.push()
--offset drawable position (or draw hex tile at position):
love.graphics.translate( ixOffset+j*jxOffset, i*iyOffset )
--draw poly-line between all hex points - creating hex
love.graphics.line( hex[1].x,hex[1].y, hex[2].x,hex[2].y, hex[3].x,hex[3].y, hex[4].x,hex[4].y, hex[5].x,hex[5].y, hex[6].x,hex[6].y, hex[1].x,hex[1].y );
love.graphics.pop()
j = j+1
until j > 5
--invert for each new line
--keep lines from increasing x coordinates
ixOffset = -ixOffset
i = i+1
until i > 5
end
------------------------------
Excuse me if i made mistake somewhere, ran the code and worked properly on my side. If you discover anything off, comment and i will try to fix it.
Related
I'm trying to make a freecam sort of tool for a rather obscure game. I'm trying to figure out how to move the XY position of the freecam based on the car's rotation value, but I'm struggling to do so. I've tried using Calculating X Y movement based on rotation angle? and modifying it a bit, but it doesn't work as intended. The game's rotation uses a float that ranges from -1 to 1, -1 being 0 degrees and 1 being 360 degrees.
Putting rot at -1 corresponds to X+
Putting rot at 0 corresponds to Z+
Putting rot at 1 corresponds to X-
Here's my cheat engine code:
speed = 10000
local yaw = math.rad((180*(getAddressList().getMemoryRecordByDescription('rot').Value))+180)
local px = getAddressList().getMemoryRecordByDescription('playerx').Value
local py = getAddressList().getMemoryRecordByDescription('playery').Value
local pz = getAddressList().getMemoryRecordByDescription('playerz').Value
local siny = math.sin(yaw) -- Sine of Horizontal (Yaw)
local cosy = math.cos(yaw) -- Cosine of Horizontal (Yaw)
getAddressList().getMemoryRecordByDescription('playerx').Value = ((getAddressList().getMemoryRecordByDescription('playerx').Value)+(cosy*speed))
getAddressList().getMemoryRecordByDescription('playerz').Value = ((getAddressList().getMemoryRecordByDescription('playerz').Value)+(siny*speed))
print(yaw)
Demo almost (?) working example: https://ellie-app.com/4h9F8FNcRPya1/1
For demo: Click to draw ray, and rotate camera with left and right to see ray. (As the origin is from the camera, you can't see it from the position it is created)
Context
I am working on an elm & elm-webgl project where I would like to know if the mouse is over an object when clicked. To do is I tried to implement a simple ray cast. What I need is two things:
1) The coordinate of the camera (This one is easy)
2) The coordinate/direction in 3D space of where was clicked
Problem
The steps to get from 2D view space to 3D world space as I understand are:
a) Make coordinates to be in a range of -1 to 1 relative to view port
b) Invert projection matrix and perspective matrix
c) Multiply projection and perspective matrix
d) Create Vector4 from normalised mouse coordinates
e) Multiply combined matrices with Vector4
f) Normalise result
Try so far
I have made a function to transform a Mouse.Position to a coordinate to draw a line to:
getClickPosition : Model -> Mouse.Position -> Vec3
getClickPosition model pos =
let
x =
toFloat pos.x
y =
toFloat pos.y
normalizedPosition =
( (x * 2) / 1000 - 1, (1 - y / 1000 * 2) )
homogeneousClipCoordinates =
Vec4.vec4
(Tuple.first normalizedPosition)
(Tuple.second normalizedPosition)
-1
1
inversedProjectionMatrix =
Maybe.withDefault Mat4.identity (Mat4.inverse (camera model))
inversedPerspectiveMatrix =
Maybe.withDefault Mat4.identity (Mat4.inverse perspective)
inversedMatrix2 =
Mat4.mul inversedProjectionMatrix inversedPerspectiveMatrix
to =
Vec4.vec4
(Tuple.first normalizedPosition)
(Tuple.second normalizedPosition)
1
1
toInversed =
mulVector inversedMatrix2 to
toNorm =
Vec4.normalize toInversed
toVec3 =
vec3 (Vec4.getX toNorm) (Vec4.getY toNorm) (Vec4.getZ toNorm)
in
toVec3
Result
The result of this function is that the rays are too much to the center to where I click. I added a screenshot where I clicked in all four of the top face of the cube. If I click on the center of the viewport the ray will be correctly positioned.
It feels close, but not quite there yet and I can't figure out what I am doing wrong!
After trying other approaches I found a solution:
getClickPosition : Model -> Mouse.Position -> Vec3
getClickPosition model pos =
let
x =
toFloat pos.x
y =
toFloat pos.y
normalizedPosition =
( (x * 2) / 1000 - 1, (1 - y / 1000 * 2) )
homogeneousClipCoordinates =
Vec4.vec4
(Tuple.first normalizedPosition)
(Tuple.second normalizedPosition)
-1
1
inversedViewMatrix =
Maybe.withDefault Mat4.identity (Mat4.inverse (camera model))
inversedProjectionMatrix =
Maybe.withDefault Mat4.identity (Mat4.inverse perspective)
vec4CameraCoordinates = mulVector inversedProjectionMatrix homogeneousClipCoordinates
direction = Vec4.vec4 (Vec4.getX vec4CameraCoordinates) (Vec4.getY vec4CameraCoordinates) -1 0
vec4WorldCoordinates = mulVector inversedViewMatrix direction
vec3WorldCoordinates = vec3 (Vec4.getX vec4WorldCoordinates) (Vec4.getY vec4WorldCoordinates) (Vec4.getZ vec4WorldCoordinates)
normalizedVec3WorldCoordinates = Vec3.normalize vec3WorldCoordinates
origin = model.cameraPos
scaledDirection = Vec3.scale 20 normalizedVec3WorldCoordinates
destination = Vec3.add origin scaledDirection
in
destination
I left it as verbose as possible, if someone finds I use incorrect terminology please make a comment and I will update the answer.
I am sure there are lots of optimisations possible (Multiplying matrices before inverting or combining some of the steps.)
Updated the ellie app here: https://ellie-app.com/4hZ9s8S92PSa1/0
I was wondering how you would go about this assuming you were working with a 2D coordinate frame in pixels. I created some examples of what I mean:
Red dot represents the origin point
Grey circle shows the radius but wouldn't actually be drawn
Green dots have a set amount and get evenly distributed along the
circle
With 3 dots:
http://prntscr.com/5vbj86
With 8 dots:
http://prntscr.com/5vbobd
Spektre answered my question but in C++, here it is in lua for anyone interested:
local x,y
local n = 10
local r = 100.0
local x0 = 250.0
local y0 = 250.0
local da = 2.0 * math.pi/n
local a = 0.0
for i = 0, n - 1 do
x = x0 + r * math.cos(a)
y = y0 + r * math.sin(a)
-- draw here using x,y
a = a + da
end
on circle very easy
for evenly distributed points the angle is increasing with the same step
so for N points the step is da=2.0*M_PI/N;
The code in C++ is like this:
int i,n=10;
double x,y,a,da;
double r=100.0,x0=250.0,y0=250.0; // circle definition
da=2.0*M_PI/double(n);
for (a=0.0,i=0;i<n;i++,a+=da)
{
x=x0+r*cos(a);
y=y0+r*sin(a);
// here draw or do something with (x,y) point
}
I'd need some help from an "iso guru". I am fiddling on a game where there are two cannons placed on an isometric grid. When one cannon fires a bullet, it should fly in a curved trajectory, like shown below. While this would be an easy task on an x/y plane, I have no clue how to calculate a curved path (with variable height) on an isometric plane.
Could someone point me into the right direction please? I'd need to fire bullets from one field to any given other, while the bullets' flying altitude (the "strength" of the curve) depends on the given shot power.
Any hints? :(
Image: http://postimg.org/image/6lcqnwcrr/
This may help. The trajectory function takes some trajectory parameters (velocity, elevation, starting position and gravity) and returns a function that calcs the y position from the x position in world space.
The converter returns a function that converts between world and screen co-ords for a given projection angle.
What follows is an example of it being used to calculate the trajectory for some points in screen space.
It's really for indication purposes. It has a bunch of potential divide by zeroes but it generates trajectories that look ok for sensible elevations, projections and velocities.
-- A trajectory in world space
function trajectory(v,elevation,x0,y0,g)
x0 = x0 or 0
y0 = y0 or 0
local th = math.rad(elevation or 45)
g = g or 9.81
return function(x)
x = x-x0
local a = x*math.tan(th)
local b = (g*x^2)/(2*(v*math.cos(th))^2)
return y0+a-b
end
end
-- convert between screen and world
function converter(iso)
iso = math.rad(iso or 0)
return function(toscreen,x,y)
if toscreen then
y = y+x*math.sin(iso)
x = x*math.cos(iso)
else
x = x/math.cos(iso)
y = y-x*math.sin(iso)
end
return x,y
end
end
-- velocity 60m/s at an angle of 70 deg
t = trajectory(60,70,0,0)
-- iso projection of 30 deg
c = converter(30)
-- x in screen co-ords
for x = 0,255 do
local xx = c(false,x,0) -- x in world co-ords
local y = t(xx) -- y in world co-ords
local _,yy = c(true,xx,y) -- y in screen co-ords
local _,y0 = c(true,xx,0) --ground in screen co-ords
yy = math.floor(yy) -- not needed
if yy>y0 then print(x,yy) end -- if it's above ground
end
If there are no lateral forces you can use the 2D equation for ballistic motion in XZ plane (so y=0 at all time), then rotate via a 3D transformation around z axis to account for actual orientation of canon in 3D space. This transformation matrix is very simple, you can unfold the multiplication (write out the terms multiplied) to get the 3D equations.
I have an OpenGL program (written in Delphi) that lets user draw a polygon. I want to automatically revolve (lathe) it around an axis (say, Y asix) and get a 3D shape.
How can I do this?
For simplicity, you could force at least one point to lie on the axis of rotation. You can do this easily by adding/subtracting the same value to all the x values, and the same value to all the y values, of the points in the polygon. It will retain the original shape.
The rest isn't really that hard. Pick an angle that is fairly small, say one or two degrees, and work out the coordinates of the polygon vertices as it spins around the axis. Then just join up the points with triangle fans and triangle strips.
To rotate a point around an axis is just basic Pythagoras. At 0 degrees rotation you have the points at their 2-d coordinates with a value of 0 in the third dimension.
Lets assume the points are in X and Y and we are rotating around Y. The original 'X' coordinate represents the hypotenuse. At 1 degree of rotation, we have:
sin(1) = z/hypotenuse
cos(1) = x/hypotenuse
(assuming degree-based trig functions)
To rotate a point (x, y) by angle T around the Y axis to produce a 3d point (x', y', z'):
y' = y
x' = x * cos(T)
z' = x * sin(T)
So for each point on the edge of your polygon you produce a circle of 360 points centered on the axis of rotation.
Now make a 3d shape like so:
create a GL 'triangle fan' by using your center point and the first array of rotated points
for each successive array, create a triangle strip using the points in the array and the points in the previous array
finish by creating another triangle fan centered on the center point and using the points in the last array
One thing to note is that usually, the kinds of trig functions I've used measure angles in radians, and OpenGL uses degrees. To convert degrees to radians, the formula is:
degrees = radians / pi * 180
Essentially the strategy is to sweep the profile given by the user around the given axis and generate a series of triangle strips connecting adjacent slices.
Assume that the user has drawn the polygon in the XZ plane. Further, assume that the user intends to sweep around the Z axis (i.e. the line X = 0) to generate the solid of revolution, and that one edge of the polygon lies on that axis (you can generalize later once you have this simplified case working).
For simple enough geometry, you can treat the perimeter of the polygon as a function x = f(z), that is, assume there is a unique X value for every Z value. When we go to 3D, this function becomes r = f(z), that is, the radius is unique over the length of the object.
Now, suppose we want to approximate the solid with M "slices" each spanning 2 * Pi / M radians. We'll use N "stacks" (samples in the Z dimension) as well. For each such slice, we can build a triangle strip connecting the points on one slice (i) with the points on slice (i+1). Here's some pseudo-ish code describing the process:
double dTheta = 2.0 * pi / M;
double dZ = (zMax - zMin) / N;
// Iterate over "slices"
for (int i = 0; i < M; ++i) {
double theta = i * dTheta;
double theta_next = (i+1) * dTheta;
// Iterate over "stacks":
for (int j = 0; j <= N; ++j) {
double z = zMin + i * dZ;
// Get cross-sectional radius at this Z location from your 2D model (was the
// X coordinate in the 2D polygon):
double r = f(z); // See above definition
// Convert 2D to 3D by sweeping by angle represented by this slice:
double x = r * cos(theta);
double y = r * sin(theta);
// Get coordinates of next slice over so we can join them with a triangle strip:
double xNext = r * cos(theta_next);
double yNext = r * sin(theta_next);
// Add these two points to your triangle strip (heavy pseudocode):
strip.AddPoint(x, y, z);
strip.AddPoint(xNext, yNext, z);
}
}
That's the basic idea. As sje697 said, you'll possibly need to add end caps to keep the geometry closed (i.e. a solid object, rather than a shell). But this should give you enough to get you going. This can easily be generalized to toroidal shapes as well (though you won't have a one-to-one r = f(z) function in that case).
If you just want it to rotate, then:
glRotatef(angle,0,1,0);
will rotate it around the Y-axis. If you want a lathe, then this is far more complex.