Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411419 Posts in 69363 Topics- by 58416 Members - Latest Member: timothy feriandy

April 17, 2024, 09:02:17 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)General thread for quick questions
Pages: 1 ... 56 57 [58] 59 60 ... 69
Print
Author Topic: General thread for quick questions  (Read 134400 times)
qMopey
Level 6
*


View Profile WWW
« Reply #1140 on: July 25, 2017, 02:22:31 PM »

Ok, I ported the monogame math API (matrices, quaternions, vectors) to my engine. But, I was wondering, what's the best way to represent the object position/rotation/scale (that works for both 2D and 3D)?

Would this suffice?
Code:
struct ctransform {
    cvector3 pos;
    cvector3 scale;
    cquaternion rotation;
};

Then, in the rendering, I would make 3 matrices from those and multiply the end result.

Close. It will work. If you want improvements this is probably the best:

Code:
struct ctransform {
    cvector3 pos;
    cmatrix3 rotation;
};

Generally scale is not that important for physics/pathfinding, and only really important as one-off things in rendering. For instance in physics scale will be implied by the shapes in local space. For pathfinding and the like, usually there are polygons already in world space (or that just need to be translated/rotated).

Non-uniform scaling can break down nice and clean matrix hierarchies since you can get non-linear transformations. Losing linearity can be a big performance problem. To deal with this a lot of systems just tack on a uniform scaling (or sometimes non-uniform scaling) as a final processing step. This scale factor is not apart of a lower level math API, and is specific to the system using it.

The above transform I showed is an affine transformation. They can be combined in stacks to form clean linear transformations. This is the most useful abstraction for general 3D math in games, in my experience.

Don't store a quaternion since most rendering using rotation matrices (like the GPU). Storing a and converting a quaternion is super annoying. The most common use case of a rotation matrix is to do a matrix vector product, something that is kind of awkward to perform with a raw quaternion.

Finally, write some Mul functions.

Code:
ctransform Mul( ctransform tx, cvector3 v );
ctransform Mul( cvector3 v, ctransform tx );
ctransform MulT( ctransform tx, cvector3 v );
ctransform MulT( cvector3 v, ctransform tx );

The reason Mul functions like these are nice is they can internally respect a consistent product orientation (column/row major notation), but allow the user to perform math however they are most comfortable. In the end it has no performance penalty. The T stands for transpose and can perform an optimized lower-level primitive to do Matrix^T * Vector transformation without performing an explicit transpose operation.

These kinds of functions play very nicely with SIMD APIs in both 2D and 3D, since new kinds of primitives can be added as needed. Make some more overloads for Mul that can transform planes, triangles, quads, OBBs, or other implicitly defined shapes (like spheres/capsules). Since scale is not included generic Mul functions can transform all shapes in a consistent matter (due to preserving linearity).

The nicest thing about this style is any other new primitives can be added as needed, on any platform that can run C/C++. If documented well people can come along and add in new features when needed. Like new shape types, or new kinds of functions.

Here's an example use case not possible with your struct, but possible when scale is removed:

Code:
ctransform a = GetA( );
ctransform b = GetB( );
ctransform b_in_a = MulT( a, b );
cvector3 v_in_b = GetVertexInB( );
cvector3 v_in_a = Mul( b_in_a, v_in_b );
bool hit = HitTestInA( GetAGeometry( ), v_in_a );
if ( hit ) { do stuff ... }

The code gets the reference frame of two objects A and B. A vertex from B is transformed into A and tested on the geometry defined local to A. It is important to do this test local to A, since converting a single point from space B to A is much cheaper than transforming all of A's geometry into a new space.

GetA( ) can look like this inside:

Code:
ctransform GetA( )
{
    return Mul( a, Mul( b, Mul( c, d ) ) );
}

Since linearity is preserved, stacks of matrices can be joined to represent new reference frames arbitrarily. In practice GetA would probably just returned a cached matrix as the result of all those Mul functions.

Anyways, hope that helps! I'm actually implementing a bunch of this stuff at work right now since the code base lacks a consistent and well formed API.

Edit: Oh and don't make 3 unique matrices and multiply them. You should know enough about transformations to create your 4x4 matrix on the spot in one go. The upper left 3x3 sub-matrix is a rotation matrix. For scaling, scale the x, y and z columns for x y and z scaling factors. Translation goes in the upper right column, and a 1 goes in the bottom right corner. The bottom left 3 elements (the bottom row) are zeroes.

Place all the things in the right spot. Don't take an unnecessary performance hit by constructing 3 individual matrices and then multiplying them.
« Last Edit: July 25, 2017, 02:37:55 PM by qMopey » Logged
ferreiradaselva
Level 3
***



View Profile
« Reply #1141 on: July 25, 2017, 04:23:33 PM »

Thanks so much for the detailed explanation!

I'm gonna go with what you suggested, without the scale and the rotation as matrix. I hadn't even thought of the annoyance that is converting quaternion to matrix and vice versa. My decision was solely based on the fact that Unity stores the rotation internally as a quaternion, at least according with the docs.

Finally, write some Mul functions.

Code:
ctransform Mul( ctransform tx, cvector3 v );
ctransform Mul( cvector3 v, ctransform tx );
ctransform MulT( ctransform tx, cvector3 v );
ctransform MulT( cvector3 v, ctransform tx );

Code:
ctransform a = GetA( );
ctransform b = GetB( );
ctransform b_in_a = MulT( a, b );
cvector3 v_in_b = GetVertexInB( );
cvector3 v_in_a = Mul( b_in_a, v_in_b );
bool hit = HitTestInA( GetAGeometry( ), v_in_a );
if ( hit ) { do stuff ... }

Those functions look really useful. As I still don't have any convenience function for the `transform`, those seems to be a good start point.

Quote
These kinds of functions play very nicely with SIMD APIs in both 2D and 3D, since new kinds of primitives can be added as needed. Make some more overloads for Mul that can transform planes, triangles, quads, OBBs, or other implicitly defined shapes (like spheres/capsules). Since scale is not included generic Mul functions can transform all shapes in a consistent matter (due to preserving linearity).

I've heard that including scale in the multiplication could have undesired results, but I didn't know that was the reason.

Quote
Edit: Oh and don't make 3 unique matrices and multiply them. You should know enough about transformations to create your 4x4 matrix on the spot in one go. The upper left 3x3 sub-matrix is a rotation matrix. For scaling, scale the x, y and z columns for x y and z scaling factors. Translation goes in the upper right column, and a 1 goes in the bottom right corner. The bottom left 3 elements (the bottom row) are zeroes.

Place all the things in the right spot. Don't take an unnecessary performance hit by constructing 3 individual matrices and then multiplying them.

Yup. I will soon change from using multiple matrices to a single matrix when I'm more confortable with it (and when everything else is working fine).

Quote
Anyways, hope that helps! I'm actually implementing a bunch of this stuff at work right now since the code base lacks a consistent and well formed API.

It definitelly helps, thanks a lot!
Logged

Photon
Level 4
****


View Profile
« Reply #1142 on: August 11, 2017, 07:27:10 AM »

I know very little about working with bytes in high-level languages and I'm not sure where to start. I'm using Haxe, if that means anything.

Specifically, I'm looking to convert some music files to bytes and use the bytes files in the final build of my game so I can keep the music "hidden" from the enduser. Help?
Logged
JWki
Level 4
****


View Profile
« Reply #1143 on: August 11, 2017, 09:06:53 AM »

I know very little about working with bytes in high-level languages and I'm not sure where to start. I'm using Haxe, if that means anything.

Specifically, I'm looking to convert some music files to bytes and use the bytes files in the final build of my game so I can keep the music "hidden" from the enduser. Help?

Converting music files to bytes is a bit redundant as music files are bytes already - everything consists of bytes. Every piece of data that you store in some form of memory is nothing but a stream of bits, so 0s and 1s, and eight of those make up a byte.
Files are already bytes, but they can have different formats, meaning that different bytes mean different things - for example, you can have a format that contains a number of positions, stored as 3 * 4 bytes each. How a file is interpreted depends on the program consuming it. Files that are identified by windows as "music" are just files that have a certain extension that identifies them as wave files, flak files, whatever.

So looking at your goal here, if you want to "hide" the music from the end user, you want to convert it into a format that he cannot use outside of your game. To cut things short, don't worry about that - music is one of the things that games do not usually store in a custom format because the formats that we have are good enough already. Some games might package them, but usually they are just .wav files or whatever, packed together into a single file with a header that tells the game where to find which file. Essentially, trying to hide music files from the player makes little to no sense because it is A LOT of work with little gain.
Logged
qMopey
Level 6
*


View Profile WWW
« Reply #1144 on: August 11, 2017, 12:24:46 PM »

Can't you just embed some C code into haxe? Since it cross-compiles? Or something like that.
Logged
InfiniteStateMachine
Level 10
*****



View Profile
« Reply #1145 on: August 12, 2017, 06:28:24 PM »

Maybe you could store it in one of these http://api.haxe.org/haxe/io/UInt8Array.html sort of like you would in c/C++ inside a header or cpp file.
Logged

Garthy
Level 9
****


Quack, verily


View Profile WWW
« Reply #1146 on: August 13, 2017, 12:34:41 AM »


I personally have a file-to-code Ruby script whose sole purpose is to read in binary data and output a C declaration of the form:

Code:
const size_t foo_size = ...;
const uint8_t foo_data[foo_size] = { ... };

It is an insanely useful thing to include in your build process for embedding arbitrary binary data into an executable.

A similar thing could be done to emit haxe I imagine. The UInt8Array suggestion above looks to be the key.

Logged
oahda
Level 10
*****



View Profile
« Reply #1147 on: August 13, 2017, 12:58:39 AM »

What are the pros and cons to embedding resources directly in code versus loading them from file at runtime? It does seem very handy to embed everything from a portability standpoint, as there is no need to mess with complicated differences in filesystems (especially on things like mobile and consoles), and the size footprint is going to be there anyway, but what are some negatives about it?
Logged

Crimsontide
Level 5
*****


View Profile
« Reply #1148 on: August 13, 2017, 02:47:46 AM »

What are the pros and cons to embedding resources directly in code versus loading them from file at runtime? It does seem very handy to embed everything from a portability standpoint, as there is no need to mess with complicated differences in filesystems (especially on things like mobile and consoles), and the size footprint is going to be there anyway, but what are some negatives about it?

The obvious cons are:

- It bloats the exe size, this is rarely a problem on modern systems but can be an issue if you start heading into the gigabyte+ sizes.  Could be an issue on mobile and consoles, depending on how their virtual memory/paging system works.  Worst case scenario you could end up loading everything into memory twice, first when you run the executable, the second when you load that asset into the GPU or whatever, since the original isn't necessarily unloaded.

- Updates/patches can become quite large, a small change could require essentially the entire program and all its resource to be re-sent.

- Nearly impossible to mod.

- Debugging can easily become a nightmare if you're not very careful about your build system.

- Can significantly increase build times (in particular link times).

I like to embed my shaders into the exe, but the rest of my assets I leave in archives grouped according to use.
Logged
oahda
Level 10
*****



View Profile
« Reply #1149 on: August 13, 2017, 02:51:07 AM »

Thought so. Thanks!
Logged

Garthy
Level 9
****


Quack, verily


View Profile WWW
« Reply #1150 on: August 13, 2017, 03:03:53 AM »

what are some negatives about it?

A few:

- It is sitting in memory all the time, even if you don't use it. This can be an issue for huge files.
- Whatever you plan to use it with needs to be comfortable accepting a memory block or you need to write an adapter that converts it into a suitable form.
- Can't pass it as an argument to an external program because it doesn't really exist as a file.
- Need a to rebuild the exe every time you change a resource (there are ways around this). Becomes an issue during development.
- Not user editable (can be a pro or con).

I frequently use the technique myself, tend avoid it for large files, and *always* have an option to use external files during development.
Logged
Polly
Level 6
*



View Profile
« Reply #1151 on: August 13, 2017, 03:56:28 PM »

It is sitting in memory all the time, even if you don't use it.

This is incorrect. Resources don't get loaded into memory automatically. A executable that takes up 1MB of RAM doesn't take up any more RAM regardless of whether it has 7KB or 7GB of resources embedded.

Updates/patches can become quite large, a small change could require essentially the entire program and all its resource to be re-sent.

There's no real difference between external files and internal resources when it comes to updates / patches.
Logged
Garthy
Level 9
****


Quack, verily


View Profile WWW
« Reply #1152 on: August 13, 2017, 05:16:02 PM »


It is sitting in memory all the time, even if you don't use it.

This is incorrect. Resources don't get loaded into memory automatically. A executable that takes up 1MB of RAM doesn't take up any more RAM regardless of whether it has 7KB or 7GB of resources embedded.

No, it is not incorrect, but being a simplification, it is incomplete. Think of memory in this context as RAM backed with swap if it helps. Do note that the entirety of each resource is not getting swapped out either, and even that is dependent on how the operating system manages it. There's more to it than that (eg. initial loading of the program), but that's where I'll leave it for now.

Updates/patches can become quite large, a small change could require essentially the entire program and all its resource to be re-sent.

There's no real difference between external files and internal resources when it comes to updates / patches.

Changing the size of a single embedded resource or *anything* stored as data causes a cascade of changes elsewhere in the executable depending on where the compiler located it. A decent patcher can mitigate this somewhat. Whilst you cross your fingers and hope that the patcher figures it out, the worst case is exactly as Crimsontide describes. Also: Execution time of a patch creation scales very badly as the executable grows in size. Source: Have written an executable patcher.
Logged
flex$
Level 2
**



View Profile
« Reply #1153 on: September 16, 2017, 05:44:33 PM »

Hey everyone, my game (GM8.1) has rarely been throwing up 'floating point division by zero' errors, and while I believed to have pinned down at least the event triggering the error, one of my users today reported receiving it on start-up. I've COMBED code searching for ANYWHERE a zero might be being divided, but, nothing. Google is relatively dry as it relates to this error + GameMaker 8. I'm pretty stumped and would really appreciate any insight. Is there a source of this error I might be overlooking? Why is it so rare and random?
Logged

cynicalsandel
Level 7
**



View Profile
« Reply #1154 on: September 23, 2017, 11:23:13 AM »

I'm coding in Pico-8 and I ran into a problem. There are the init, draw, and update functions. I declare a player x and y position in the init and have it change in the update based on button input. What I want to do is have the player x and y position do a huge change just one time when the game changes state (basically teleport the player and have them be able to move normally after that), but if i change the those variables in update or draw based on the gamestate/scene change, it ends up locking the player in one position because it changes those x and y values every frame. Also, if instead I draw the sprite at player_x+56, player_x+112 for instance, it circumvents the movement/collision code, so I specifically need to change those variables.

edit: i figured out a roundabout way of doing it so don't worry about me
« Last Edit: September 23, 2017, 10:55:44 PM by cynicalsandel » Logged

qMopey
Level 6
*


View Profile WWW
« Reply #1155 on: September 24, 2017, 01:35:56 PM »

edit: i figured out a roundabout way of doing it so don't worry about me

Was the solution to use an if-statement, or something more obscure? Smiley
Logged
cynicalsandel
Level 7
**



View Profile
« Reply #1156 on: September 24, 2017, 02:45:23 PM »

create a variable that i can change in each scene/state and add it to both the the player x and y coordinates and the collision check coordinates. sort of an offset type of deal.
Logged

qMopey
Level 6
*


View Profile WWW
« Reply #1157 on: September 24, 2017, 02:47:49 PM »

create a variable that i can change in each scene/state and add it to both the the player x and y coordinates and the collision check coordinates. sort of an offset type of deal.

You're implementing a rendering pipeline! But your transformation from "model to world" is just a translation for now  Big Laff
Logged
ThemsAllTook
Administrator
Level 10
******



View Profile WWW
« Reply #1158 on: October 14, 2017, 08:41:13 AM »

I'm doing some work with 3D models. My knowledge and code are both a bit outdated, so I'm trying to get up to speed with modern workflows and concepts. I'm currently implementing a glTF loader, and trying to base my data models on its concepts.

In the spec, it defines a "tangent" vec4 as a core vertex attribute. I'm familiar with all of the other attributes listed here, but this one is a new concept for me: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes

What are vertex tangents, and how are they used in rendering?
Logged

JWki
Level 4
****


View Profile
« Reply #1159 on: October 14, 2017, 08:48:07 AM »

I'm doing some work with 3D models. My knowledge and code are both a bit outdated, so I'm trying to get up to speed with modern workflows and concepts. I'm currently implementing a glTF loader, and trying to base my data models on its concepts.

In the spec, it defines a "tangent" vec4 as a core vertex attribute. I'm familiar with all of the other attributes listed here, but this one is a new concept for me: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes

What are vertex tangents, and how are they used in rendering?

https://learnopengl.com/#!Advanced-Lighting/Normal-Mapping

This article explains it well.
Logged
Pages: 1 ... 56 57 [58] 59 60 ... 69
Print
Jump to:  

Theme orange-lt created by panic