Skip to content

Translation notice

This page was translated with machine translation and may contain inaccuracies. If you can help improve it, please open an issue or submit a pull request.

A feasible method to convert OBJ model to json model

Xuanyu1725

Xuanyu1725

keywords OBJ model, json model, Minecraft, geometry structure conversion, voxelization, lightweight model, tangent space

Overview and background ​

This article aims to study a feasible high-precision, nearly lossless and lightweight method of converting OBJ (Wavefront) to JSON model (Minecraft style) to help artists quickly import the project into the Minecraft scene after completing the design in a general modeling software.

There are significant differences between the two models we considered. The OBJ model defines the geometric structure of the object by vertices and surfaces, with triangular surface meshes as the core (some software also supports polygonal surfaces), and can represent complex geometric structures (such as curved surfaces) with high precision and low polygon count. Minecraft's json model is defined by voxels that have been translated, rotated, and scaled, and usually cannot represent complex surfaces well, or even triangles. Without considering textures and maps, we will next discuss a feasible method to convert OBJ models to json models with acceptable visual effects errors.

Traditional implementation of model voxelization ​

Method introduction ​

The traditional way to convert an OBJ model to a voxelized model similar to a json model is to approximate the geometry of the OBJ model with axis-aligned cubes (voxels). The basic idea of ​​this method is:

  1. Place the OBJ model into a 3D mesh

  2. Determine the minimum subdivision mesh

  3. For each grid cell, check if it intersects the OBJ model, and if so, fill a voxel

  4. Optimize the generated voxel model to reduce the number of voxels

  5. Get the final voxel model

Shortcomings of the method ​

The implementation of this traditional method is simple, but it has shortcomings that cannot be ignored:

  1. The visual error produced by this method is too large (we have not yet defined the error), especially for curved and sloped surfaces, and voxelized models tend to appear very rough.

  2. Achieving high visual accuracy requires the use of very fine meshes, resulting in a massive increase in voxel count and making the model very large and difficult to use in Minecraft.

  3. Even if optimization is performed, such as merging some voxels, or using different levels of detail (LOD) at different locations, the generated voxel model may still contain a large number of redundant voxels, which affects performance and is not conducive to modification by artists.

Improvement method ​

Define error ​

In order to better measure the difference between the converted json model and the original OBJ model, and for us to implement the optimization algorithm, we need to define the error values ​​before and after conversion.

Volume error ​

For the error caused by volume fitting (such as traditional methods), we can use the distortion volume to measure it. The distortion volume is defined as the difference between the union volume of the two models and their intersection volume. Since we will eventually observe the model at various angles, we need to calculate the distortion volume projection area under different viewing angles and take the average value as the final error indicator.

Research points out that convex polyhedron in three-dimensional space isSO(3)SO(3)The average projected area under the action and the surface area of ​​the polyhedron are constant values.14\frac{1}{4}For concave polyhedrons (the more general case) this conclusion no longer holds since the faces will occlude each other, but we can approximate the average projected area by sampling multiple viewing angles, and its upper bound is still14\frac{1}{4}, in order to simplify the calculation, we use an upper bound to measure the error.

Right now,

eV=S4≥14π∫S2A(u) dω,e_V = \frac{S}{4} \geq \frac{1}{4 \pi} \int_{S^2} A(\boldsymbol{u}) \,\mathrm d \omega,

inSSis the surface area of ​​the distortion volume,A(u)A(\boldsymbol{u})for perspectiveu\boldsymbol{u}The projected area under , integrated over the entire unit sphereS2S^2proceed on. We useS4\tfrac{S}{4}As an upper bound on the simplified error, this simplifies calculations while ensuring conservative estimates.

surface error ​

Errors caused by surface fittingeSe_S(As we will introduce next), we can use the distortion surface area to measure, the distortion surface area is defined as the absolute value of the difference between the surface areas of the two models.

Similarly, we do not consider the influence of occlusion and directly use the sum of the distortion surface area of ​​each subdivision surface as the error metric. The specific calculation formula is related to the fitting method, which we will introduce next, but the idea is similar to the volume error.

Optimization direction ​

Model complexity can be measured by the number of voxels. We hope that the error and complexity are as small as possible, but the two are often contradictory. Define a cost index that is proportional to the error and complexity (weighted sum measurement is used here), that is

J=αe+βM,J = \alpha e + \beta M,

ineeis the error index,MMis the number of voxels,α,β\alpha, \betais the weight coefficient. We hope that through optimization algorithms, we can makeJJminimize.

A review of improved voxelization methods ​

Since Minecraft's json model is defined based on rotatable voxels, we can take advantage of this to better fit the geometry of the OBJ model by allowing the voxels to rotate, thereby reducing errors.

For the original OBJ model, we define connected, parallel triangles as a surface. Triangles in the surface satisfy the following conditions:

  1. Adjacent triangles in the surface have at least two shared vertices

  2. The normal directions of all triangles in the surface are the same (i.e. parallel), and the angle error must be small enough to be considered in actual implementation.

  3. Triangles that meet the above two conditions also belong to the surface if they are adjacent to triangles in the same surface.

For each surface, we calculate its tangent space (tangent space), that is, define a local coordinate system so that the normal direction of the surface is aligned with an axis of the local coordinate system. We then voxelize in this tangent space, using axis-aligned voxels to approximate the geometry of the surface.

Specific implementation steps ​

Read OBJ model ​

This step is relatively simple. The OBJ file format is a text format. You can use existing libraries (such as TinyOBJLoader) to parse OBJ files. You only need to extract vertex, normal and face index information. In the file, respectivelyv n fThe first lines represent vertex, normal, and face indices.

Sometimes the normal information may not exist or be unreliable, we can calculate the normal from the vertex and face information. For each triangular face, we can calculate the normal using the cross product:

N=(v2−v1)×(v3−v1)∥(v2−v1)×(v3−v1)∥\boldsymbol{N} = \frac{(\boldsymbol{v}_2 - \boldsymbol{v}_1) \times (\boldsymbol{v}_3 - \boldsymbol{v}_1)}{\|(\boldsymbol{v}_2 - \boldsymbol{v}_1) \times (\boldsymbol{v}_3 - \boldsymbol{v}_1)\|}

Sometimes polygonal faces appear in the OBJ model (such as the model exported by Blender). We can triangulate the polygonal faces and convert them into multiple triangular faces for processing.

Construct surface connected components ​

We traverse all the triangular faces we read, create connected components according to the above definition, and obtain multiple surfaces, each of which is an undirected graph.

Compute tangent space ​

In each surface, we take the normal of that surface as the normal of the tangent spaceN\boldsymbol{N}, take a vector that is not collinear with the normal as the tangent directionT\boldsymbol{T}(such as the sides of any triangle, normalize them), and then calculate the direction of the bitangent through the cross product

B=N×T\boldsymbol{B} = \boldsymbol{N} \times \boldsymbol{T}

Three vectors form an orthogonal local coordinate system, and we construct a transformation matrixMTBN\boldsymbol{M}_{TBN}, convert the global coordinate system into a tangent space coordinate system:

MTBN=[TBN]\boldsymbol{M}_{TBN} = \begin{bmatrix}\boldsymbol{T} & \boldsymbol{B} & \boldsymbol{N}\end{bmatrix}

Since this matrix is ​​orthogonal, its inverse exists and is equal to its transpose:

MTBN−1=MTBNT\boldsymbol{M}_{TBN}^{-1} = \boldsymbol{M}_{TBN}^T

coordinate all vertices of the surfacevi\boldsymbol{v}_iTransform to tangent space through this matrix:

vi′=MTBNvi\boldsymbol{v}_i' = \boldsymbol{M}_{TBN} \boldsymbol{v}_i

Find the optimal rectangle in tangent space ​

In the tangent space, we need to find an optimal rectangle to initially fill the interior of the surface, so that after deleting the area of ​​​​this rectangle, the number of remaining triangles is minimal. At the same time, since the error in this step can be avoided, we cannot accept any error, that is, to satisfy the rectangle, the conditions must be met:

  1. eS=0e_S = 0
  2. Rectangles that satisfy the condition are contained inside the surface.
  3. Due to artistic requirements, the side length of the rectangle needs to be the smallest element.δ\deltaan integer multiple of

Intuitively, the larger the rectangular area that meets such conditions, the better, but it is smaller than the maximum rectangular area that can be placed in the boundary of the surface. At the same time, there may be certain restrictions on angles and positions to minimize the remaining triangular surfaces.

We can find the optimal rectangle through enumeration. The specific steps are as follows (it can be replaced by a more efficient method to improve performance):

  1. Extract the boundary vertices of a surface in tangent space
  2. Align each edge with the coordinate axis respectively, and then useδ\deltaFor LOD, mesh the area within the boundary (only meshes completely contained within the boundary are retained)
  3. For each grid cell, try making it the lower left corner of a rectangle, then enumerate the possible widths and heights (bothδ\deltaan integer multiple of ), check whether the rectangle is completely contained within the bounds
  4. For a rectangle that meets the conditions, calculate the number of remaining triangles after deleting the rectangle, and record the rectangle with the smallest number of remaining triangles as the current optimal solution.

Note that after deleting the rectangle, the remaining areas may have non-triangular faces. We need to re-triangulate these areas to ensure that all remaining areas are composed of triangular faces. And for the remaining triangles, we need to recalculate their connected components, and the final measure is the total number of remaining triangle faces.

After determining the rectangle, we multiply it by leftMTBN−1\boldsymbol{M}_{TBN}^{-1}Convert it back to the global coordinate system to obtain a voxelized block (flattened voxel) of the surface.

Process remaining triangles ​

For triangular surfaces, since there is a minimum elementδ\delta, we first discuss its size. If the length and width of the smallest circumscribed rectangle of the triangle are less thanδ\delta, then the triangle is called small; if the length and width of the largest inscribed rectangle of the triangle are greater than or equal toδ\delta, the triangle is called large; otherwise it is called medium.

  1. For small triangles, we try two fitting methods:

    1. Fit the triangle using two bounding rectangles. For the vertex corresponding to the largest angle, place two rectangles parallel to the adjacent sides of the vertex inside the triangle so that the inner sides of the two rectangles intersect at a point on the opposite side of the vertex. By taking different intersection points, different fits can be obtained, and the method with the smallest error can be selected. The dimensions of the bounding rectangle need not beδ\deltaAn integer multiple of , but if the solution can be obtainedδ\deltais an integer multiple of , then selectδ\deltacan minimize the error in integer multipleseSe_Ssolution.

    2. Calculate the center of gravity of the triangle, and then use the center of gravity as the center directly using aδ×δ\delta \times \deltaThe rectangle fits the triangle, one side coincides with the side of the triangle, and the error is calculatedeSe_S。

error hereeSe_Sdefined as a set{S:S∈Svoxel,S∉Striangle}\{S: S \in S_{voxel}, S \notin S_{triangle}\}of measure14\frac{1}{4}, that is, the area of ​​the rectangle beyond the triangle:

$$e_S = \frac{1}{4} \sum_{S \in S_{voxel}, S \notin S_{triangle}} Area(S)$$

Choose the one with the smaller error among the two methods as the fitting method for the small triangle.

  1. For medium triangles, we only consider the method of using two surrounding rectangles. The process is similar to that of small triangles.

  2. For large triangles, we first fit the triangle with three bounding rectangles. Place a rectangle from three vertices along the adjacent sides, with thicknesses ofd1,d2,d3d_1, d_2, d_3. This creates an inner region that we voxelize (pixel) traditionally, but requires each rectangle to be of sizeδ\deltaAn integer multiple of , and merge adjacent rectangles after processing. At this timed1,d2,d3d_1, d_2, d_3Let the internal region have a solution andeSe_SThe smallest value, similarly, if the solution can be obtainedδ\deltais an integer multiple of , then selectδ\deltacan minimize the error in integer multipleseSe_Ssolution.

To find all rectangles, we multiply them by leftMTBN−1\boldsymbol{M}_{TBN}^{-1}Convert it back to the global coordinate system to obtain the voxelized block of the remaining triangular faces of the surface.

Below we discuss the coverage methods mentioned above in detail:

Double hemming method: ​

alt text

The double wrapping method can fit small triangles well under normal circumstances.

alt text

alt text

In special cases, it can be found that when the vertices of the rectangle are fixed on the three corners of the triangle, it will be impossible to fit, and the rectangle needs to be extended.

Note: Although this situation can always be avoided by adjusting the position of point D, we are not sure yet whether limiting the position of D is better than allowing the rectangle to be extended, so we choose to allow the rectangle to be extended.

Therefore, the logic of the double wrapping method suitable for general situations is:

  1. Select the maximum angle, recorded asAA, the adjacent angle is recorded asBBandCC, the opposite sides of the three angles are written asa,b,ca, b, c。

  2. existaaTake a little bitDD, PassDDdob,cb,cperpendicular to , respectively from the rayBA,CABA, CAintersection pointE,FE, F。

  3. byEEFor example, ifEEexistbbabove, then the rectangleR1R_1An edge ofABAB, the length of the adjacent side perpendicular to it isd1d_1Its value is equal toDDandbbdistance; ifEEexistbbalongBABAOn the extension line of the direction, the rectangleR1R_1An edge ofBEBE, the length of its perpendicular adjacent side is alsod1d_1。

To simplify calculations, we assumed1,d2d_1,d_2Can't even get itδ\deltainteger multiples of , so that they are continuous values. After calculating the best value, try to adjust it toδ\deltaan integer multiple of.

So,d1d_1The difference from the final value is not greater thanΔd1=δ\Delta d_1 = \delta, the same reasond2d_2The difference from the final value is not greater thanΔd2=δ\Delta d_2 = \delta。

In general calculations, the angleBBrectangle onR1R_1For example, it is easy to calculate the distortion area as:

S(R1)=12d12tan⁡B S(R_1) = \frac{1}{2} d_1^2 \tan B

In the special case where the rectangle needs to be extended, the distortion area is:

S(R1)=12(d12tan⁡B+(cot⁡Cd1−b)2tan⁡(B+C)) S(R_1) = \frac{1}{2} (d_1^2 \tan B + (\cot C d_1 - b)^2 \tan(B+C))

And the critical condition iscot⁡Cd1−b=0\cot C d_1 - b = 0, when the left side of the equal sign is positive,DDThe projection ofBCBCOn the extension line of , when it is negative,DDThe projection ofBCBCsuperior.

So we can use amax()function to uniformly represent the distortion area:

S(R1)=12(d12tan⁡B+(max⁡(0,cot⁡Cd1−b))2tan⁡(B+C)) S(R_1) = \frac{1}{2} \left( d_1^2 \tan B + (\max(0, \cot C d_1 - b))^2 \tan(B+C) \right)

The sum of the areas on both sides does not overlap, so the total distortion area is:

S=S(R1)+S(R2) S = S(R_1) + S(R_2)

The error is the distortion area14\frac{1}{4} :

eS=S4=S(R1)+S(R2)4 e_S = \frac{S}{4} = \frac{S(R_1) + S(R_2)}{4}

And the constraints areϕ(d1,d2)=d1sin⁡B+d2sin⁡C−a=0\phi (d_1, d_2) = d_1 \sin B + d_2 \sin C - a = 0

Using the Lagrange multiplier method, we construct the Lagrange function:

L(d1,d2,λ)=eS(d1,d2)+λϕ(d1,d2) \mathcal{L}(d_1, d_2, \lambda) = e_S(d_1, d_2) + \lambda \phi(d_1, d_2)

rightd1,d2,λd_1, d_2, \lambdaTaking the partial derivative and setting it to zero gives us a system of equations:

∂L∂d1=0,∂L∂d2=0,∂L∂λ=0\frac{\partial \mathcal{L}}{\partial d_1} = 0, \quad \frac{\partial \mathcal{L}}{\partial d_2} = 0, \quad \frac{\partial \mathcal{L}}{\partial \lambda} = 0

Notice,L\mathcal{L}It is not differentiable at the critical point, so we need to discuss it case by case:

It is easy to prove that only one side needs to extend the rectangle, that is, there are only three situations:

In a given condition that satisfies the constraintsd1,d2d_1, d_2,likecot⁡Cd1−b≤0\cot C d_1 - b \leq 0andcot⁡Bd2−c≤0\cot B d_2 - c \leq 0, then it is a general situation (the critical point is regarded as a general situation);

likecot⁡Cd1−b>0\cot C d_1 - b > 0andcot⁡Bd2−c≤0\cot B d_2 - c \leq 0, then it is extendedR1R_1situation;

likecot⁡Cd1−b≤0\cot C d_1 - b \leq 0andcot⁡Bd2−c>0\cot B d_2 - c > 0, then it is extendedR2R_2situation.

Substituting the three situations into the above system of equations respectively, find candidate solutions, then calculate the error, and select the solution with the smallest error as the final solution. At this time it willd1,d2d_1, d_2Adjust toδ\deltais an integer multiple of , and the solution with the smallest adjusted error is selected as the final solution (this may not always be possible).

wraparound ​

The logic of the wrap-around method suitable for large triangles is:

  1. We use different LOD to determine the internal area, that is, discretize the internal area asξδ×ξδ\xi\delta \times \xi\deltagrid,ξ\xiPick1,2,…,k1, 2, \dots, k,inkkis the side length of the largest inscribed rectangle of a triangle andδ\deltaThe integer part of the ratio. For the "large triangle" in our definition,k≥1k \ge 1。

  2. Under different LODs, pixelate the internal area (inscribed fitting), that is, within the divided grid, select the grid completely contained within the triangle as the pixel block.

  3. Select three vertices of the triangleA,B,CA, B, C, place three rectangles along the adjacent sides.R1,R2,R3R_1, R_2, R_3, whose thicknesses are respectivelyd1,d2,d3d_1, d_2, d_3. Choose the smallestd1,d2,d3d_1, d_2, d_3Cover the area that cannot be covered by inscribed fitting, and calculate the distortion area caused by wrappingSS。

For this step, we need to first select the direction, that is, select one side as the bottom edge. At this time, the determined pixels will generate the left, right, and top borders. For the hypotenuse on the left, we calculate the distance between each grid point of the left boundary and the upper boundary from the hypotenuse, and take the minimum value asd1d_1, similarly the hypotenuse on the right takes the minimum value asd2d_2, obviously the bottomd3d_3is 0.

Calculate different sides as bases separatelyd1,d2,d3d_1, d_2, d_3, and then calculate the distortion areaSS, the choice makesSSminimal solution.

Next we discuss the distortion areaSScalculation method.

alt text

Consider the general wraparound method, assumingd1,d2,d3d_1, d_2, d_3are the thickness of the rectangle on the left, right and bottom sides respectively,A,B,CA, B, Care the angles of the left, right and bottom sides respectively, then the distortion areaSSIt can be expressed as:

S=12((d12+d22)cot⁡A+(d12+d32)cot⁡B+(d22+d32)cot⁡C) S = \frac{1}{2} \left((d_1^2 + d_2^2)\cot A + (d_1^2 + d_3^2)\cot B + (d_2^2 + d_3^2)\cot C\right)

alt text

When considering that the vertex angle is an obtuse angle, it is obvious that there is no error area near the obtuse angle. Taking the right side as an example, the area of ​​the red triangle can be calculated as

SΔ=12d12cot⁡(B+C)=−12d12cot⁡AS_\Delta = \frac{1}{2}d_1^2 \cot(B+C) = - \frac{1}{2} d_1^2 \cot A

This is just the opposite of the distortion area term we originally calculated here for the rectangle on the right. If we apply the original formula, this term is just the negative value of the area of ​​the red triangle, so we need to remove it.

passmaxrestrictcot⁡A,cot⁡B,cot⁡C\cot A, \cot B, \cot CObtain the corrected distortion area:

S=12((d12+d22)max⁡(0,cot⁡A)+(d12+d32)max⁡(0,cot⁡B)+(d22+d32)max⁡(0,cot⁡C)) S = \frac{1}{2} \left((d_1^2 + d_2^2)\max(0, \cot A) + (d_1^2 + d_3^2) \max(0, \cot B) + (d_2^2 + d_3^2) \max(0, \cot C)\right)

  1. Calculate the error value at each LODeS=S4e_S = \frac{S}{4}and number of voxelsMM, choose such that the cost indexJ=αeS+βM\mathcal{J} = \alpha e_S + \beta MThe smallest solution is taken as the final solution.

In this algorithm, the optimal solution is uniquely determined by LOD becauseeSe_SandMMare all determined by LOD, so the cost function can be expressed asJ(\mathcal{J}\left(\right. LOD )\left.\right). Since LOD can only take discrete values1,2,…,k1, 2, \dots , k, we can find the optimal solution by traversing all possible LODs.

Description of voxel blocks ​

Now for all the rectangles that have been determined, we need to convert them into the format required by the Minecraft json model. The voxel definition in the Minecraft json model is slightly different on different platforms, but it mainly consists of the following information (we do not consider textures):

  1. Voxel position: Defines the position of the voxel in three-dimensional space. in Bedrock Edition voxel definitionoriginArrays and Java EditionfromThe array is at this location.

  2. Voxel size: Define the length, width, and height of the voxel. in Bedrock Edition voxel definitionsizeThe array is of this size. For Java Edition,toAn array is the sum of position and size.

  3. Rotation of the voxel: Defines the rotation angle of the voxel relative to its local coordinate system, which is described by the rotation Euler angles of the three axes. in Bedrock Edition voxel definitionrotationArrays and Java Editionrotation.x, rotation.y, rotation.zThe key is this rotation information.

  4. Rotation center: Defines the reference point for voxel rotation. in Bedrock Edition voxel definitionpivotArrays and Java Editionrotation.originThe object is this center of rotation.

In order to confirm these four pieces of information, we need to confirm the transformation matrix from the unit cube to the target rectangle.Mvoxel\boldsymbol{M}_{voxel}。

The order of voxel transformation is: first scale, then translate, and finally rotate around the rotation center by 3 Euler angles. Therefore, the transformation matrix can be expressed as:

f:R3→PRP−1TSR3f: \mathbb{R}^3 \rightarrow \boldsymbol{P}\boldsymbol{R}\boldsymbol{P}^{-1}\boldsymbol{T}\boldsymbol{S}\mathbb{R}^3

inP\boldsymbol{P}Represents the translation matrix that moves the center of rotation to the origin,R\boldsymbol{R}A matrix representing rotation around the origin. The order of rotation for each axis isX→Y→ZX \rightarrow Y \rightarrow Z,T\boldsymbol{T}represents the translation matrix,S\boldsymbol{S}Represents a scaling matrix.

For the rectangular vertices we get, we first calculate the minimum pointvmin\boldsymbol{v}_{min}, the vector formed by it and adjacent verticesdx=v1−vmin,dy=v2−vmin\boldsymbol{d}_x = \boldsymbol{v}_1 - \boldsymbol{v}_{min}, \quad \boldsymbol{d}_y = \boldsymbol{v}_2 - \boldsymbol{v}_{min}

You can get the position and size of the voxel:

T=[100vmin,x010vmin,y001vmin,z0001],T−1=[100−vmin,x010−vmin,y001−vmin,z0001]\boldsymbol{T} = \begin{bmatrix} 1 & 0 & 0 & \boldsymbol{v}_{min,x} \\ 0 & 1 & 0 & \boldsymbol{v}_{min,y} \\ 0 & 0 & 1 & \boldsymbol{v}_{min,z} \\ 0 & 0 & 0 & 1 \end{bmatrix}, \boldsymbol{T}^{-1} = \begin{bmatrix} 1 & 0 & 0 & -\boldsymbol{v}_{min,x} \\ 0 & 1 & 0 & -\boldsymbol{v}_{min,y} \\ 0 & 0 & 1 & -\boldsymbol{v}_{min,z} \\ 0 & 0 & 0 & 1 \end{bmatrix}

S=[∥dx∥0000∥dy∥0000000001]\boldsymbol{S} = \begin{bmatrix} \|d_x\| & 0 & 0 & 0 \\ 0 & \|d_y\| & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1\end{bmatrix}

becaused1,d2d_1, d_2It is always orthogonal, and the direction and size do not change after the minimum point is translated to the origin, and can be used directly for calculation. We just need to find the rotation matrix that aligns its normal with a vectorR\boldsymbol{R}That’s it. Assume that the normal line isN\boldsymbol{N}, we can calculate the rotation matrix through the following steps:

  1. Calculate axis of rotationA=N×Z\boldsymbol{A} = \boldsymbol{N} \times \boldsymbol{Z},inZ=(0,0,1)\boldsymbol{Z} = (0, 0, 1)It is based on the global coordinate systemZZaxis.

  2. Calculate rotation angleθ=arccos⁡(N⋅Z)\theta = \arccos(\boldsymbol{N} \cdot \boldsymbol{Z})。

  3. Construct a rotation matrix using Rodrigues' rotation formulaR\boldsymbol{R}:

R=I+sin⁡(θ)[A]×+(1−cos⁡(θ))[A]×2\boldsymbol{R} = \boldsymbol{I} + \sin(\theta) [\boldsymbol{A}]_{\times} + (1 - \cos(\theta)) [\boldsymbol{A}]_{\times}^2

in[A]×[\boldsymbol{A}]_{\times}is a vectorA\boldsymbol{A}antisymmetric matrix.

Finally, we can decompose the rotation matrix byR\boldsymbol{R}to get the Euler angles about each axis.

RememberX、Y、ZX、Y、ZThe Euler angle of rotation of the axis isrx,ry,rzrx, ry, rz, explicitly writeR\boldsymbol{R}as follows:

R=[cos⁡(rz)cos⁡(ry)cos⁡(rz)cos⁡(ry)sin⁡(rx)−sin⁡(rz)cos⁡(rx)cos⁡(rz)sin⁡(ry)cos⁡(rx)+sin⁡(rz)sin⁡(rx)0sin⁡(rz)cos⁡(ry)sin⁡(rz)sin⁡(ry)sin⁡(rx)+cos⁡(rz)cos⁡(rx)sin⁡(rz)sin⁡(ry)cos⁡(rx)−cos⁡(rz)sin⁡(rx)0−sin⁡(rx)cos⁡(ry)sin⁡(rx)cos⁡(ry)cos⁡(rx)00001]\boldsymbol{R} = \begin{bmatrix} \cos(rz)\cos(ry) & \cos(rz)\cos(ry)\sin(rx)-\sin(rz)\cos(rx) & \cos(rz)\sin(ry)\cos(rx)+\sin(rz)\sin(rx) & 0 \\ \sin(rz)\cos(ry) & \sin(rz)\sin(ry)\sin(rx)+\cos(rz)\cos(rx) & \sin(rz)\sin(ry)\cos(rx)-\cos(rz)\sin(rx) & 0 \\ - \sin(rx) & \cos(ry)\sin(rx) & \cos(ry)\cos(rx) & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix}

Euler angles can be extracted from the matrix:

rememberR\boldsymbol{R}of theiiOKjjThe column elements areRij\boldsymbol{R}_{ij}, then there is:

ry=arcsin⁡(−R31)ry = \arcsin(-\boldsymbol{R}_{31})

Since gimbal deadlock may occur, we need toryrySelect different calculation methods for the value:

Define a smaller threshold$$\varepsilon = 10^{-6}$$

when∣R31∣<1−ε\left|\boldsymbol{R}_{31}\right| \lt 1 - \varepsilonWhen , no universal joint deadlock occurs, and the remaining Euler angles are calculated directly:

rx=arctan⁡2(R32cos⁡(ry),R33cos⁡(ry))rx = \arctan2\left(\frac{\boldsymbol{R}_{32}}{\cos(ry)}, \frac{\boldsymbol{R}_{33}}{\cos(ry)}\right)

rz=arctan⁡2(R21cos⁡(ry),R11cos⁡(ry))rz = \arctan2\left(\frac{\boldsymbol{R}_{21}}{\cos(ry)}, \frac{\boldsymbol{R}_{11}}{\cos(ry)}\right)

when∣R31∣≥1−ε\left|\boldsymbol{R}_{31}\right| \geq 1 - \varepsilonWhen , a universal joint deadlock occurs and other methods need to be used to calculate Euler angles.

\qquadset uprz=0rz = 0

\qquadlikeR31<0\boldsymbol{R}_{31} \lt 0:

ry=π2ry = \frac{\pi}{2}

rx=arctan⁡2(R12,R13)rx = \arctan2(\boldsymbol{R}_{12}, \boldsymbol{R}_{13})

\qquadotherwise:

ry=−π2ry = -\frac{\pi}{2}

rx=arctan⁡2(−R12,−R13)rx = \arctan2(-\boldsymbol{R}_{12}, -\boldsymbol{R}_{13})

In fact, since the rotation center and rotation axis are arbitrary, there are multiple possible solutions to achieve the same visual effect. In our calculation, the rotation center is the smallest point of the voxel.

Therefore, the four pieces of information we need are as follows:

Bedrock Edition voxel definition:

json
{
  "origin": [v_min.x, v_min.y, v_min.z],
  "size": [|d_x|, |d_y|, 0],
  "rotation": [rx_in_degrees, ry_in_degrees, rz_in_degrees],
  "pivot": [v_min.x, v_min.y, v_min.z]
}

Java version voxel definition:

json
{
  "from": [v_min.x, v_min.y, v_min.z],
  "to": [v_min.x + |d_x|, v_min.y + |d_y|, v_min.z],
  "rotation": {
    "origin": [v_min.x, v_min.y, v_min.z],
    "x": rx,
    "y": ry,
    "z": rz
  }
}

Just write it directly in json format.

remaining questions ​

Although we proposed a feasible method to convert OBJ models to json models, there are still some remaining issues that need further research and resolution:

  1. The algorithm for finding the optimal rectangle described in the article is relatively complex, especially when dealing with complex surfaces. A more efficient algorithm may be needed to speed up the process.

  2. We regard triangular surfaces as independent from other triangular surfaces. Artists usually hide large chunks of errors in invisible areas (such as closed interiors), thereby reducing the number of cubes and achieving relatively small errors. We have not considered this yet.

  3. The current method mainly takes error as the optimization goal, and in most cases cannot optimize the voxel number (cube number) to the same low level as manual production. Compared with models manually made by artists, automatically converted models usually have higher errors and block numbers.

  4. For very complex geometries, current methods may still fail to achieve ideal visual effects, and the combination of other techniques, such as subdivision surfaces or multi-resolution representations, can be considered in the future to further improve the conversion quality.

But all in all, this tangent space-based voxelization method provides a promising solution for high-precision, lightweight OBJ to json model conversion. According to the ideas in the article, a practical conversion tool can be written to help artists apply design results to Minecraft scenes more efficiently.

Powered by VitePress and GitHub Pages