[064] How to Transform UV Coordinates in CityEngine CGA

Understanding UV transformations in Esri CityEngine to rotate, scale, translate, tile, and delete texture mapping coordinates using CGA rules.

This tutorial covers essential UV manipulation commands in CityEngine CGA, including rotateUV, scaleUV, translateUV, normalizeUV, tileUV, and deleteUV, enabling precise texture placement and alignment on 3D procedural geometries.

This is the final lesson on texture control.

Today, we will learn about commands to control texture rotation, scale, position, and more after applying textures using projectUV().

You might wonder why separate commands exist when size or position can be configured in setupProjection().

However, strictly speaking, the commands used here are not applied directly to the texture itself, but rather to the UV coordinates of the texture. They transform the UV coordinates assigned to the texture.

When we generally perform modeling in 3D programs, the coordinate system consists of X, Y, and Z.

Textures are also mapped and applied based on a coordinate system, but to avoid confusion with the XYZ coordinate system, the UVW coordinate system is used.

Modeling uses XYZ, while textures use UVW.

The commands we will learn today—such as rotateUV, scaleUV, translateUV, normalizeUV, and deleteUV—modify these UV coordinates.

As a result, changes occur together with the texture mapped onto the UV coordinates, making it visually appear as if the texture itself is changing.

The phrase "changes occur together with the texture mapped onto the UV coordinates" means that transformations must be applied after executing projectUV() to apply the texture.

In other words,


@Angle
attr UV_value = 0

Lot -->
	setupProjection(0,scope.xz,scope.sx,scope.sz,0,0)
	texture("images/texture.jpg")
	projectUV(0)
	rotateUV(0,UV_value)
	X.

It means that rotateUV() for the corresponding uvSet must come after projectUV() has been executed.

If rotateUV on line 9 is written before projectUV, no change will occur.

Do not forget this. It is important.

Let's take a look at the reference for each command.


First is rotateUV.

ArcGIS CityEngine rotateUV reference page
figure 2 : rotateUV CGA command reference (https://doc.arcgis.com/en/cityengine/latest/cga/cga-rotate-uv.htm)

It is simple.

You only need to specify the target uvSet and the angle to rotate.

The center of rotation is the origin of the UV coordinate system.


@Angle
attr UV_value = 0

Lot -->
	setupProjection(0,scope.xz,scope.sx,scope.sz,0,0)
	texture("images/texture.jpg")
	projectUV(0)
	rotateUV(0,UV_value)
	X.

Type the code above and try moving the slider bar.


Next, let's look at scaleUV().

ArcGIS CityEngine scaleUV reference page
figure 4 : scaleUV CGA command reference (https://doc.arcgis.com/en/cityengine/latest/cga/cga-scale-uv.htm)

The explanation sounds complicated, but think of it simply.

It increases the layout density of the texture by the values entered in uFactor and vFactor.

In other words, if it is written as scaleUV(0, 2, 2), it does not mean expanding the size of the texture to double its width and height.

It means tiling the texture twice as densely compared to what was defined in setupProjection().

What if it is scaleUV(0, 0.5, 0.5)?

That's right. It applies the texture at half the density configured in setupProjection(). Naturally, the visible texture size will become larger.

Type the code below yourself and try moving the slider bar.


@Range(min=0.1, max=5, stepsize=0.1)
attr UV_value = 1

Lot -->
	setupProjection(0,scope.xz,scope.sx,scope.sz,0,0)
	texture("images/texture.jpg")
	projectUV(0)
	scaleUV(0,UV_value,UV_value)
	X.

Cityengine scaleUV() Demonstration

Does that make sense?


Next is translateUV().

ArcGIS CityEngine translateUV reference page
figure 6 : translateUV CGA command reference (https://doc.arcgis.com/en/cityengine/latest/cga/cga-translate-uv.htm)

It follows a similar format.

You specify the uvSet and offset it in the U and V directions. (The offset range is from 0 to 1.)

However, the unit used for offsetting here is not the distance unit used in the project,

but a relative distance after setting the full span of the U and V directions to 1 each.

If it says translateUV(0, 0.5, 0.1),

it means move uvSet 0 by 50% of the U range and 10% of the V range.

Shall we look at the code?


@Range(min=0, max=1, stepsize=0.1)
attr U_offset = 0
@Range(min=0, max=1, stepsize=0.1)
attr V_offset = 0 

Lot -->
	setupProjection(0,scope.xz,scope.sx,scope.sz,0,0)
	texture("images/texture.jpg")
	projectUV(0)
	translateUV(0,U_offset,V_offset)
	X.

CityEngine translateUV() Demonstration

Although it appears as if the texture is moving, strictly speaking, it is the UV coordinates that are changing.


Next is normalizeUV().

ArcGIS CityEngine normalizeUV reference page
figure 8 : normalizeUV CGA command reference (https://doc.arcgis.com/en/cityengine/latest/cga/cga-normalize-uv.htm)

It is a bit complex.

This command does not need to be used when there is only a single isolated face; it is used when multiple faces are connected or form a 3D shape.

It combines multiple faces into a single UV space or separates UVs independently for each face.

I have rarely used this.


@Enum("uv","uvUniform","u","v")
attr mode = "uv"
@Enum("separatePerFace","collectiveAllFaces")
attr type = "separatePerFace"

Lot -->
	extrude(10)
	setupProjection(0,scope.xz,scope.sx,scope.sz,0,0)
	texture("images/texture.jpg")
	projectUV(0)
	normalizeUV(0, mode, type)
	X.

Cityengine normalizeUV() Demonstration


Next is tileUV().

ArcGIS CityEngine tileUV reference page
figure 10 : tileUV CGA command reference (https://doc.arcgis.com/en/cityengine/latest/cga/cga-tile-uv.htm)

This command repeats the texture like tiling so that top, bottom, left, and right edges fit perfectly to the texture size.

If you look at the red underlined area, you can see '~' and '\'' that we learned during the split lesson. This means mapping can be performed using approximate values and ratios.

Note!! Although the command syntax is similar, textureWidth and textureHeight entered here use real-world distance units used in the project. It does not touch UV coordinates directly, but transforms the actual texture size. Keep this in mind.


@Range(min=1, max=10, stepsize=0.5)
attr tileX = 1
@Range(min=1, max=10, stepsize=0.5)
attr tileY = 1

Lot -->
	extrude(10)
	setupProjection(0,scope.xz,scope.sx,scope.sz,0,0)
	texture("images/texture.jpg")
	projectUV(0)
	tileUV(0, ~tileX, ~tileY)
	X.

Cityengine tileUV() Demonstration

You can confirm that the size is precisely adjusted to fit the overall UV size.


Finally, here is deleteUV().

ArcGIS CityEngine deleteUV reference page
figure 12 : deleteUV CGA command reference (https://doc.arcgis.com/en/cityengine/latest/cga/cga-delete-uv.htm)

It is simple. It deletes the specified uvSet.


@Angle
attr UVrotate = 0

Lot -->
	extrude(10)
	setupProjection(0,scope.xz,scope.sx,scope.sz,0,0)
	texture("images/texture.jpg")
	projectUV(0)
	rotateUV(0, UVrotate)
	A

A -->
	case UVrotate >90 :
		deleteUV(0)
		X.
	else : X.

If the UV rotation value exceeds 90, the texture is deleted.

Cityengine deleteUV() Demonstration


At last~ we have completed all basic CGA commands and concepts.

From next time, we will take a photo of a simple building and walk through the process of modeling it step by step.

That's all for today~

Continue Learning
Explore complete tutorial collections for CityEngine and Global Mapper.

Comments

Popular posts from this blog

029] How to Split Polygon Areas with Intersecting Lines in Global Mapper

[008] How to Understand CGA Rule Structure in CityEngine (Beginner Guide)

046] How to Create Parallel Offset Lines in Global Mapper