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)The average projected area under the action and the surface area of ​​the polyhedron are constant values.14For 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, in order to simplify the calculation, we use an upper bound to measure the error.

Right now,

eV=S414πS2A(u)dω,

inSis the surface area of ​​the distortion volume,A(u)for perspectiveuThe projected area under , integrated over the entire unit sphereS2proceed on. We useS4As an upper bound on the simplified error, this simplifies calculations while ensuring conservative estimates.

surface error

Errors caused by surface fittingeS(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,

ineis the error index,Mis the number of voxels,α,βis the weight coefficient. We hope that through optimization algorithms, we can makeJminimize.

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=(v2v1)×(v3v1)(v2v1)×(v3v1)

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, take a vector that is not collinear with the normal as the tangent directionT(such as the sides of any triangle, normalize them), and then calculate the direction of the bitangent through the cross product

B=N×T

Three vectors form an orthogonal local coordinate system, and we construct a transformation matrixMTBN, convert the global coordinate system into a tangent space coordinate system:

MTBN=[TBN]

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

MTBN1=MTBNT

coordinate all vertices of the surfaceviTransform to tangent space through this matrix:

vi=MTBNvi

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=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.δan 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δFor 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δan 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 leftMTBN1Convert 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δ, we first discuss its size. If the length and width of the smallest circumscribed rectangle of the triangle are less thanδ, 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δ, 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δAn integer multiple of , but if the solution can be obtainedδis an integer multiple of , then selectδcan minimize the error in integer multipleseSsolution.

    2. Calculate the center of gravity of the triangle, and then use the center of gravity as the center directly using aδ×δThe rectangle fits the triangle, one side coincides with the side of the triangle, and the error is calculatedeS

error hereeSdefined as a set{S:SSvoxel,SStriangle}of measure14, 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,d3. This creates an inner region that we voxelize (pixel) traditionally, but requires each rectangle to be of sizeδAn integer multiple of , and merge adjacent rectangles after processing. At this timed1,d2,d3Let the internal region have a solution andeSThe smallest value, similarly, if the solution can be obtainedδis an integer multiple of , then selectδcan minimize the error in integer multipleseSsolution.

To find all rectangles, we multiply them by leftMTBN1Convert 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 asA, the adjacent angle is recorded asBandC, the opposite sides of the three angles are written asa,b,c

  2. existaTake a little bitD, PassDdobcperpendicular to , respectively from the rayBA,CAintersection pointE,F

  3. byEFor example, ifEexistbabove, then the rectangleR1An edge ofAB, the length of the adjacent side perpendicular to it isd1Its value is equal toDandbdistance; ifEexistbalongBAOn the extension line of the direction, the rectangleR1An edge ofBE, the length of its perpendicular adjacent side is alsod1

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

So,d1The difference from the final value is not greater thanΔd1=δ, the same reasond2The difference from the final value is not greater thanΔd2=δ

In general calculations, the angleBrectangle onR1For example, it is easy to calculate the distortion area as:

S(R1)=12d12tanB

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

S(R1)=12(d12tanB+(cotCd1b)2tan(B+C))

And the critical condition iscotCd1b=0, when the left side of the equal sign is positive,DThe projection ofBCOn the extension line of , when it is negative,DThe projection ofBCsuperior.

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

S(R1)=12(d12tanB+(max(0,cotCd1b))2tan(B+C))

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

S=S(R1)+S(R2)

The error is the distortion area14 :

eS=S4=S(R1)+S(R2)4

And the constraints areϕ(d1,d2)=d1sinB+d2sinCa=0

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

L(d1,d2,λ)=eS(d1,d2)+λϕ(d1,d2)

rightd1,d2,λTaking the partial derivative and setting it to zero gives us a system of equations:

Ld1=0,Ld2=0,Lλ=0

Notice,LIt 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,d2,likecotCd1b0andcotBd2c0, then it is a general situation (the critical point is regarded as a general situation);

likecotCd1b>0andcotBd2c0, then it is extendedR1situation;

likecotCd1b0andcotBd2c>0, then it is extendedR2situation.

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,d2Adjust toδis 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ξδ×ξδgrid,ξPick1,2,,k,inkis the side length of the largest inscribed rectangle of a triangle andδThe integer part of the ratio. For the "large triangle" in our definition,k1

  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,C, place three rectangles along the adjacent sides.R1,R2,R3, whose thicknesses are respectivelyd1,d2,d3. Choose the smallestd1,d2,d3Cover the area that cannot be covered by inscribed fitting, and calculate the distortion area caused by wrappingS

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 asd1, similarly the hypotenuse on the right takes the minimum value asd2, obviously the bottomd3is 0.

Calculate different sides as bases separatelyd1,d2,d3, and then calculate the distortion areaS, the choice makesSminimal solution.

Next we discuss the distortion areaScalculation method.

alt text

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

S=12((d12+d22)cotA+(d12+d32)cotB+(d22+d32)cotC)

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)=12d12cotA

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.

passmaxrestrictcotA,cotB,cotCObtain the corrected distortion area:

S=12((d12+d22)max(0,cotA)+(d12+d32)max(0,cotB)+(d22+d32)max(0,cotC))
  1. Calculate the error value at each LODeS=S4and number of voxelsM, choose such that the cost indexJ=αeS+βMThe smallest solution is taken as the final solution.

In this algorithm, the optimal solution is uniquely determined by LOD becauseeSandMare all determined by LOD, so the cost function can be expressed asJ( LOD ). Since LOD can only take discrete values1,2,,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

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:R3PRP1TSR3

inPRepresents the translation matrix that moves the center of rotation to the origin,RA matrix representing rotation around the origin. The order of rotation for each axis isXYZTrepresents the translation matrix,SRepresents a scaling matrix.

For the rectangular vertices we get, we first calculate the minimum pointvmin, the vector formed by it and adjacent verticesdx=v1vmin,dy=v2vmin

You can get the position and size of the voxel:

T=[100vmin,x010vmin,y001vmin,z0001],T1=[100vmin,x010vmin,y001vmin,z0001]S=[dx0000dy0000000001]

becaused1,d2It 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 vectorRThat’s it. Assume that the normal line isN, we can calculate the rotation matrix through the following steps:

  1. Calculate axis of rotationA=N×Z,inZ=(0,0,1)It is based on the global coordinate systemZaxis.

  2. Calculate rotation angleθ=arccos(NZ)

  3. Construct a rotation matrix using Rodrigues' rotation formulaR

R=I+sin(θ)[A]×+(1cos(θ))[A]×2

in[A]×is a vectorAantisymmetric matrix.

Finally, we can decompose the rotation matrix byRto get the Euler angles about each axis.

RememberXYZThe Euler angle of rotation of the axis isrx,ry,rz, explicitly writeRas 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)0sin(rx)cos(ry)sin(rx)cos(ry)cos(rx)00001]

Euler angles can be extracted from the matrix:

rememberRof theiOKjThe column elements areRij, then there is:

ry=arcsin(R31)

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

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

when|R31|<1εWhen , no universal joint deadlock occurs, and the remaining Euler angles are calculated directly:

rx=arctan2(R32cos(ry),R33cos(ry))rz=arctan2(R21cos(ry),R11cos(ry))

when|R31|1εWhen , a universal joint deadlock occurs and other methods need to be used to calculate Euler angles.

set uprz=0

likeR31<0:

ry=π2rx=arctan2(R12,R13)

otherwise:

ry=π2rx=arctan2(R12,R13)

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