Implemented a math library for Lua

Original link: https://blog.gotocoding.com/archives/1743?utm_source=rss&utm_medium=rss&utm_campaign=%25e7%25bb%2599lua%25e5%2586%2599%25e4%25ba%2586%25e4%25b8%2580%25e4% 25b8%25aa%25e6%2595%25b0%25e5%25ad%25a6%25e5%25ba%2593

My toy engine plans to use Lua as the first business language.

After the engine can load a scene, I need a camera controller to receive user input to move and rotate the camera to walk the scene.

I’m going to use Lua to write this logic. When calculating the Transform of the camera, certain mathematical operations are required. This requires a Lua version of the math library .

How to write a concise and efficient math library for Lua is not a problem that I started to think about recently.

When using the tolua framework in the early years, it was found that a large number of temporary objects would be generated when performing mathematical calculations in Lua, which greatly increased the burden of GC.

Although I have thought about this problem from time to time in the past two years, there has been no solution.

This time, without the burden of Unity, I hope to find a new way of thinking.

Why does writing mathematical operations in C# not generate GC? The fundamental reason is that in C# (vector3, quaternion, matrix) and other objects are all struct types, that is, value types . These objects are allocated on the stack and destroyed when the function returns. Even when the value is returned, the value is copied directly.

In Lua, there are only two types of value types in the strict sense: boolean and number. Although string behaves like a value type, temporary string objects also generate memory garbage.

So when we generally implement vector3, we will use Table or userdata to save xyz. This is because the value types in Lua are not large enough to hold so much data in xyz.

A very intuitive idea, can we extend the value type in Lua so that it can contain at most four fields of xyzw.

The answer is yes, but at a high cost. The cost of memory, the cost of performance, and the cost of maintenance.

I carefully recalled the limited client experience in the past few years, and I found that the mathematical operations were all lumped together.

In other words, our mathematical operations generally have a few finite inputs and a few finite outputs. However, the intermediate calculation process is very complicated. As long as the temporary variables generated by these intermediate processes are solved, it is basically in line with expectations.

Along this line of thought, since only numbert and boolean are value types in Lua, is it possible for me to use number to represent a vector3 or quaternion?

The answer is yes. We just need to implement an extra piece of space in C, then use an index to point to this vector3 or quaternion value and we’re done.

Based on the above ideas, I implemented a math stack. The scope of this stack can only be used within a function.

If you want to return the result of the calculation to another function, you can only take the value out of the stack and return it to the other function explicitly.

If other functions need to perform mathematical calculations again, a new math stack space needs to be opened up.

The general usage is as follows:

 local mathx = require "engine.math" local camera_up = {x = 0, y = 0, z = 0} local camera_forward = {x = 0, y = 0, z = 0} local camera_right = {x = 0, y = 0, z = 0} local stk<close> = mathx.begin() print("rotation", component.get_quaternion(self)) local rot = stk:quaternion(component.get_quaternion(self)) local up = stk:mul(rot, stk:vector3f_up()) local forward = stk:mul(rot, stk:vector3f_forward()) local right = stk:mul(rot, stk:vector3f_right()) stk:save(up, camera_up) stk:save(right, camera_right) stk:save(forward, camera_forward)

First use math.begin() to create a math stack, and then we can perform various mathematical calculations on the stack.

When the math is done, we can use stk:save to fetch the value of xyzw from the math stack.

There are two ways to use stk:save. When we pass in a table, stk will directly put the value of xyzw into the table. We can also pass in no parameters, then stk:save will return the value of xyz or xyzw according to the type of value.

The toclose feature of Lua is used here. When the stack is used up, the __close function will automatically put the stack object into the Cache.

The next time math.begin is called, it is allocated directly from the Cache, so that 0 memory allocation can be achieved.

After implementing this library, I purposely made a performance comparison with xlua.

 local stk<close> = math.begin() local v1 = stk:vector3f(xx_v3_1) local v2 = stk:vector3f(xx_v3_2) v2 = stk:vector3f_cross(v1, v2) v2 = stk:vector3f_cross(v1, v2) v2 = stk:vector3f_cross(v1, v2) v2 = stk:vector3f_cross(v1, v2) stk:save(v2, xx_v3_2)

Compared with the same logic xlua writing method, the performance is about 300% higher. And as the computing process increases, the performance advantage will become more and more obvious.

In addition, before performing mathematical calculations, we often need to obtain attributes such as position, rotation, and scale in transform.

These properties are either vector3 or quaternion. If we use table or userdata to return, it will still increase the GC burden.

Counting carefully, in fact, these data types have only 4 variables at most. Therefore, I let the function component.get_position directly return three values ​​of xyz, and component.get_rotation directly return four values ​​of xyzw.

As for whether it needs to be stored in the table, this is controlled by the business logic.

The post Implementing a math library for Lua first appeared on the Return to Chaos Blog .

This article is reproduced from: https://blog.gotocoding.com/archives/1743?utm_source=rss&utm_medium=rss&utm_campaign=%25e7%25bb%2599lua%25e5%2586%2599%25e4%25ba%2586%25e4%25b8%2580%25e4% 25b8%25aa%25e6%2595%25b0%25e5%25ad%25a6%25e5%25ba%2593
This site is for inclusion only, and the copyright belongs to the original author.

Leave a Comment