Source engine styled rope rendering - c

I am creating a 3D graphics engine and one of the requirements is ropes that behave like in Valve's source engine.
So in the source engine, a section of rope is a quad that rotates along it's direction axis to face the camera, so if the section of rope is in the +Z direction, it will rotate along the Z axis so it's face is facing the camera's centre position.
At the moment, I have the sections of ropes defined, so I can have a nice curved rope, but now I'm trying to construct the matrix that will rotate it along it's direction vector.
I already have a matrix for rendering billboard sprites based on this billboarding technique:
Constructing a Billboard Matrix
And at the moment I've been trying to retool it so that Right, Up, Forward vector match the rope segment's direction vector.
My rope is made up of multiple sections, each section is a rectangle made up of two triangles, as I said above, I can get the position and sections perfect, it's the rotating to face the camera that's causing me a lot of problems.
This is in OpenGL ES2 and written in C.
I have studied Doom 3's beam rendering code in Model_beam.cpp, the method used there is to calculate the offset based on normals rather than using matrices, so I have created a similar technique in my C code and it sort of works, at least it, works as much as I need it to right now.
So for those who are also trying to figure this one out, use the cross-product of the mid-point of the rope against the camera position, normalise that and then multiply it to how wide you want the rope to be, then when constructing the vertices, offset each vertex in either + or - direction of the resulting vector.
Further help would be great though as this is not perfect!
Thank you

Check out this related stackoverflow post on billboards in OpenGL It cites a lighthouse3d tutorial that is a pretty good read. Here are the salient points of the technique:
void billboardCylindricalBegin(
float camX, float camY, float camZ,
float objPosX, float objPosY, float objPosZ) {
float lookAt[3],objToCamProj[3],upAux[3];
float modelview[16],angleCosine;
glPushMatrix();
// objToCamProj is the vector in world coordinates from the
// local origin to the camera projected in the XZ plane
objToCamProj[0] = camX - objPosX ;
objToCamProj[1] = 0;
objToCamProj[2] = camZ - objPosZ ;
// This is the original lookAt vector for the object
// in world coordinates
lookAt[0] = 0;
lookAt[1] = 0;
lookAt[2] = 1;
// normalize both vectors to get the cosine directly afterwards
mathsNormalize(objToCamProj);
// easy fix to determine wether the angle is negative or positive
// for positive angles upAux will be a vector pointing in the
// positive y direction, otherwise upAux will point downwards
// effectively reversing the rotation.
mathsCrossProduct(upAux,lookAt,objToCamProj);
// compute the angle
angleCosine = mathsInnerProduct(lookAt,objToCamProj);
// perform the rotation. The if statement is used for stability reasons
// if the lookAt and objToCamProj vectors are too close together then
// |angleCosine| could be bigger than 1 due to lack of precision
if ((angleCosine < 0.99990) && (angleCosine > -0.9999))
glRotatef(acos(angleCosine)*180/3.14,upAux[0], upAux[1], upAux[2]);
}

Related

Generating a orthographic MVP matrix for a global shadow map

I am making a voxel-based game engine, and I am in the process of implementing shadow maps. The shadows will be directional, and affect the whole scene. The map here is 40x40 units big (from a top-down perspective, since the map is generated from a heightmap).
My plan is to put the directional light in the middle of the scene, and then change the direction of the light as needed. I do not want to recompute the shadow map according to the player's position because that requires rerendering it each frame, and I want to run this engine on very low-power hardware.
Here is my problem: I am trying to calculate the model-view-projection matrix for this world. Here is my current work, using the cglm math library:
const int light_height = 25, map_width = map_size[0], map_height = map_size[1];
const int half_map_width = map_width >> 1, half_map_height = map_height >> 1;
const float near_clip_dist = 0.01f;
// Matrix calculations
mat4 view, projection, model_view_projection;
glm_look_anyup((vec3) {half_map_width, light_height, half_map_height}, light_dir, view);
glm_ortho(-half_map_width, half_map_width, half_map_height, -half_map_height, near_clip_dist, light_height, projection);
glm_mul(projection, view, model_view_projection);
Depending on the light direction, sometimes some part of the scene are not in shadow when they should be. I think that the problem is with my call to glm_ortho, since the documentation says that the orthographic projection matrix corners should be in terms of the viewport. In that case, how do I transform world-space coordinates to view-space coordinates to calculate this matrix correctly?

Physics in 3D world in OPENGL with C language only

I've been trying to code a 3D game where the player shoots an arrow and I wanted to do the equations for the 3D. I know the equations for the 2D world where:
x = v0 * cosθ * t
y = v0 * sinθ * t - 0.5 * g * t^2
But how do I use these equations in my 3D world where I have the Z axis?
Instead of making the arrows follow an explicit curve, I suggest simulating the arrow step by step.
What you need to store is a position (with x,y,z coordinates, starting off at the archer's location) and a velocity (also with x,y,z coordinates, starting off as some constant times the direction the player is looking), and some scene gravity (also with x,y,z coordinates, but it'll probably point straight down).
When the simulation progresses by a timestep of t, add t times the velocity to the position, then add t times gravity to the velocity.
This way, you're free to do more interesting things to the arrow later, like having wind act on it (add t times wind to the velocity) or having air resistance act on it (multiply velocity by t times some value a bit smaller than 1) or redirecting it (change velocity to something else entirely) without having to recalculate the path of the arrow.

Do depth values in AVDepthData (from TrueDepth camera) indicate distance from camera or camera plane?

Do depth values in AVDepthData (from TrueDepth camera) indicate distance in meters from the camera, or perpendicular distance from the plane of the camera (i.e. z-value in camera space)?
My goal is to get an accurate 3D point from the depth data, and this distinction is important for accuracy. I've found lots online regarding OpenGL or Kinect, but not for TrueDepth camera.
FWIW, this is the algorithm I use. I'm find the value of depth buffer at a pixel found using some OpenCV feature detection. Below is the code I use to find real world 3D point at a given pixel at let cgPt: CGPoint. This algorithm seems to work quite well, but I'm not sure whether small error is introduced by the assumption of depth being distance to camera plane.
let depth = 1/disparity
let vScreen = sceneView.projectPoint(SCNVector3Make(0, 0, -depth))
// cgPt is the 2D coordinates at which I sample the depth
let worldPoint = sceneView.unprojectPoint(SCNVector3Make(cgPt.x, cgPt.y, vScreen.z))
I'm not sure of authoritative info either way, but it's worth noticing that capture in a disparity (not depth) format uses distances based on a pinhole camera model, as explained in the WWDC17 session on depth photography. That session is primarily about disparity-based depth capture with back-facing dual cameras, but a lot of the lessons in it are also valid for the TrueDepth camera.
That is, disparity is 1/depth, where depth is distance from subject to imaging plane along the focal axis (perpendicular to imaging plane). Not, say, distance from subject to the focal point, or straight-line distance to the subject's image on the imaging plane.
IIRC the default formats for TrueDepth camera capture are depth, not disparity (that is, depth map "pixel" values are meters, not 1/meters), but lacking a statement from Apple it's probably safe to assume the model is otherwise the same.
It looks like it measures distance from the camera's plane rather than a straight line from the pinhole. You can test this out by downloading the Streaming Depth Data from the TrueDepth Camera sample code.
Place the phone vertically 10 feet away from the wall, and you should expect to see one of the following:
If it measures from the focal point to the wall as a straight line, you should expect to see a radial pattern (e.g. the point closest to the camera is straight in front of it; the points furthest to the camera are those closer to the floor and ceiling).
If it measures distance from the camera's plane, then you should expect the wall color to be nearly uniform (as long as you're holding the phone parallel to the wall).
After downloading the sample code and trying it out, you will notice that it behaves like #2, meaning it's distance from the camera's plane, not from the camera itself.

With the pose between two images, how do you project a point from one scene into another?

If you have the full relative-3D values of two images looking at the same scene (relative x,y,z), along with the extrinsic/intrinsic parameters between them, how do you project the points from one scene into the other scene, in opencv?
You can't do that in general. There is an infinite number of 3D points (a line in 3d) that get mapped to one point in image space, in the other image this line won't get mapped to a single point, but a line (see the wikipedia article on epipolar geometry). You can compute the line that the point has to be on with the fundamental matrix.
If you do have a depth map, reproject the point into 3D - using the equations on the top of the opencv page on camera calibration, especially this one (it's the only one you need):
u and v are your pixel coordinates, the first matrix is your camera matrix (for the image you are looking at currently), the second one is the matrix containing the extrinsic parameters, Z you know (from your depth map), X and Y are the ones you are looking for - you can solve for those parameters, and then use the same equation to project the point into your other camera. You can probably use the PerspectiveTransform function from opencv to do the work for you, however I can't tell you from the top of my head how to build the projection matrix.
Let the extrinsic parameters be R and t such that camera 1 is [I|0] and camera 2 is [R|t]. So all you have to do is rotate and the translate point cloud 1 with R and t to have it in the same coordinate system as point cloud 2.
Let the two cameras have projection matrices
P1 = K1 [ I | 0]
P2 = K2 [ R | t]
and let the depth of a given point x1 (homogeneous pixel coordinates) on the first camera be Z, the mapping to the second camera is
x2 = K2*R*inverse(K1)*x1 + K2*t/Z
There is no OpenCV function to do this. If the relative motion of the cameras is purely rotational, the mapping becomes a homography so you can use the PerspectiveTransform function.
( Ki = [fxi 0 cxi; 0 fyi cyi; 0 0 1] )

How to program a fractal?

I do not have any experience with programming fractals. Of course I've seen the famous Mandelbrot images and such.
Can you provide me with simple algorithms for fractals.
Programming language doesn't matter really, but I'm most familiar with actionscript, C#, Java.
I know that if I google fractals, I get a lot of (complicated) information but I would like to start with a simple algorithm and play with it.
Suggestions to improve on the basic algorithm are also welcome, like how to make them in those lovely colors and such.
Programming the Mandelbrot is easy.
My quick-n-dirty code is below (not guaranteed to be bug-free, but a good outline).
Here's the outline:
The Mandelbrot-set lies in the Complex-grid completely within a circle with radius 2.
So, start by scanning every point in that rectangular area.
Each point represents a Complex number (x + yi).
Iterate that complex number:
[new value] = [old-value]^2 + [original-value] while keeping track of two things:
1.) the number of iterations
2.) the distance of [new-value] from the origin.
If you reach the Maximum number of iterations, you're done.
If the distance from the origin is greater than 2, you're done.
When done, color the original pixel depending on the number of iterations you've done.
Then move on to the next pixel.
public void MBrot()
{
float epsilon = 0.0001; // The step size across the X and Y axis
float x;
float y;
int maxIterations = 10; // increasing this will give you a more detailed fractal
int maxColors = 256; // Change as appropriate for your display.
Complex Z;
Complex C;
int iterations;
for(x=-2; x<=2; x+= epsilon)
{
for(y=-2; y<=2; y+= epsilon)
{
iterations = 0;
C = new Complex(x, y);
Z = new Complex(0,0);
while(Complex.Abs(Z) < 2 && iterations < maxIterations)
{
Z = Z*Z + C;
iterations++;
}
Screen.Plot(x,y, iterations % maxColors); //depending on the number of iterations, color a pixel.
}
}
}
Some details left out are:
1.) Learn exactly what the Square of a Complex number is and how to calculate it.
2.) Figure out how to translate the (-2,2) rectangular region to screen coordinates.
You should indeed start with the Mandelbrot set, and understand what it really is.
The idea behind it is relatively simple. You start with a function of complex variable
f(z) = z2 + C
where z is a complex variable and C is a complex constant. Now you iterate it starting from z = 0, i.e. you compute z1 = f(0), z2 = f(z1), z3 = f(z2) and so on. The set of those constants C for which the sequence z1, z2, z3, ... is bounded, i.e. it does not go to infinity, is the Mandelbrot set (the black set in the figure on the Wikipedia page).
In practice, to draw the Mandelbrot set you should:
Choose a rectangle in the complex plane (say, from point -2-2i to point 2+2i).
Cover the rectangle with a suitable rectangular grid of points (say, 400x400 points), which will be mapped to pixels on your monitor.
For each point/pixel, let C be that point, compute, say, 20 terms of the corresponding iterated sequence z1, z2, z3, ... and check whether it "goes to infinity". In practice you can check, while iterating, if the absolute value of one of the 20 terms is greater than 2 (if one of the terms does, the subsequent terms are guaranteed to be unbounded). If some z_k does, the sequence "goes to infinity"; otherwise, you can consider it as bounded.
If the sequence corresponding to a certain point C is bounded, draw the corresponding pixel on the picture in black (for it belongs to the Mandelbrot set). Otherwise, draw it in another color. If you want to have fun and produce pretty plots, draw it in different colors depending on the magnitude of abs(20th term).
The astounding fact about fractals is how we can obtain a tremendously complex set (in particular, the frontier of the Mandelbrot set) from easy and apparently innocuous requirements.
Enjoy!
If complex numbers give you a headache, there is a broad range of fractals that can be formulated using an L-system. This requires a couple of layers interacting, but each is interesting in it own right.
First you need a turtle. Forward, Back, Left, Right, Pen-up, Pen-down. There are lots of fun shapes to be made with turtle graphics using turtle geometry even without an L-system driving it. Search for "LOGO graphics" or "Turtle graphics". A full LOGO system is in fact a Lisp programming environment using an unparenthesized Cambridge Polish syntax. But you don't have to go nearly that far to get some pretty pictures using the turtle concept.
Then you need a layer to execute an L-system. L-systems are related to Post-systems and Semi-Thue systems, and like virii, they straddle the border of Turing Completeness. The concept is string-rewriting. It can be implemented as a macro-expansion or a procedure set with extra controls to bound the recursion. If using macro-expansion (as in the example below), you will still need a procedure set to map symbols to turtle commands and a procedure to iterate through the string or array to run the encoded turtle program. For a bounded-recursion procedure set (eg.), you embed the turtle commands in the procedures and either add recursion-level checks to each procedure or factor it out to a handler function.
Here's an example of a Pythagoras' Tree in postscript using macro-expansion and a very abbreviated set of turtle commands. For some examples in python and mathematica, see my code golf challenge.
There is a great book called Chaos and Fractals that has simple example code at the end of each chapter that implements some fractal or other example. A long time ago when I read that book, I converted each sample program (in some Basic dialect) into a Java applet that runs on a web page. The applets are here: http://hewgill.com/chaos-and-fractals/
One of the samples is a simple Mandelbrot implementation.
Another excellent fractal to learn is the Sierpinski Triangle Fractal.
Basically, draw three corners of a triangle (an equilateral is preferred, but any triangle will work), then start a point P at one of those corners. Move P halfway to any of the 3 corners at random, and draw a point there. Again move P halfway towards any random corner, draw, and repeat.
You'd think the random motion would create a random result, but it really doesn't.
Reference: http://en.wikipedia.org/wiki/Sierpinski_triangle
The Sierpinski triangle and the Koch curve are special types of flame fractals. Flame fractals are a very generalized type of Iterated function system, since it uses non-linear functions.
An algorithm for IFS:es are as follows:
Start with a random point.
Repeat the following many times (a million at least, depending on final image size):
Apply one of N predefined transformations (matrix transformations or similar) to the point. An example would be that multiply each coordinate with 0.5.
Plot the new point on the screen.
If the point is outside the screen, choose randomly a new one inside the screen instead.
If you want nice colors, let the color depend on the last used transformation.
I would start with something simple, like a Koch Snowflake. It's a simple process of taking a line and transforming it, then repeating the process recursively until it looks neat-o.
Something super simple like taking 2 points (a line) and adding a 3rd point (making a corner), then repeating on each new section that's created.
fractal(p0, p1){
Pmid = midpoint(p0,p1) + moved some distance perpendicular to p0 or p1;
fractal(p0,Pmid);
fractal(Pmid, p1);
}
I think you might not see fractals as an algorithm or something to program. Fractals is a concept! It is a mathematical concept of detailed pattern repeating itself.
Therefore you can create a fractal in many ways, using different approaches, as shown in the image below.
Choose an approach and then investigate how to implement it. These four examples were implemented using Marvin Framework. The source codes are available here
Here is a codepen that I wrote for the Mandelbrot fractal using plain javascript and HTML.
Hopefully it is easy to understand the code.
The most complicated part is scale and translate the coordinate systems. Also complicated is making the rainbow palette.
function mandel(x,y) {
var a=0; var b=0;
for (i = 0; i<250; ++i) {
// Complex z = z^2 + c
var t = a*a - b*b;
b = 2*a*b;
a = t;
a = a + x;
b = b + y;
var m = a*a + b*b;
if (m > 10) return i;
}
return 250;
}
The mandelbrot set is generated by repeatedly evaluating a function until it overflows (some defined limit), then checking how long it took you to overflow.
Pseudocode:
MAX_COUNT = 64 // if we haven't escaped to infinity after 64 iterations,
// then we're inside the mandelbrot set!!!
foreach (x-pixel)
foreach (y-pixel)
calculate x,y as mathematical coordinates from your pixel coordinates
value = (x, y)
count = 0
while value.absolutevalue < 1 billion and count < MAX_COUNT
value = value * value + (x, y)
count = count + 1
// the following should really be one statement, but I split it for clarity
if count == MAX_COUNT
pixel_at (x-pixel, y-pixel) = BLACK
else
pixel_at (x-pixel, y-pixel) = colors[count] // some color map.
Notes:
value is a complex number. a complex number (a+bi) is squared to give (aa-b*b+2*abi). You'll have to use a complex type, or include that calculation in your loop.
Sometimes I program fractals for fun and as a challenge. You can find them here. The code is written in Javascript using the P5.js library and can be read directly from the HTML source code.
For those I have seen the algorithms are quite simple, just find the core element and then repeat it over and over. I do it with recursive functions, but can be done differently.
People above are using finding midpoints for sierpinski and Koch, I'd much more recommend copying shapes, scaling them, and then translating them to achieve the "fractal" effect.
Pseudo-code in Java for sierpinski would look something like this:
public ShapeObject transform(ShapeObject originalCurve)
{
Make a copy of the original curve
Scale x and y to half of the original
make a copy of the copied shape, and translate it to the right so it touches the first copied shape
make a third shape that is a copy of the first copy, and translate it halfway between the first and second shape,and translate it up
Group the 3 new shapes into one
return the new shape
}

Resources