UV Coordinates Incorrect When Geometry is Far From Origin
mdeabreuatsafe opened this issue · 3 comments
We're working the SketchUp C SDK and have run into an issue when creating geometry with UV coordinates when the geometry is distant from the origin. I've modified the Texture Mapping example included in the SDK to show this issue. If we build our geometry near the origin and apply a texture using UV coordinates, everything will display correctly. This is using the texture in the sample, but the thing to notice is that everything is contiguous.
Now if we offset the geometry quite far from the origin (400 000, 5 000 000, 100; to mimic the UTM-32N coordinate system) we see that the texture has become quite disconnected.
This has been reproduced on SketchUP C SDK 2023.1.315 (what we're currently using), and the latest available 2024.0.553. This was done on Windows 11 Pro 23H2.
Here is the complete TextureMapping main.cpp, the main thing to note is the offsetting of the coordinates. Disable that line to see the correct working case, enable that line to see the broken case.
#include <SketchUpAPI/common.h>
#include <SketchUpAPI/geometry.h>
#include <SketchUpAPI/initialize.h>
#include <SketchUpAPI/model/entities.h>
#include <SketchUpAPI/model/geometry_input.h>
#include <SketchUpAPI/model/material.h>
#include <SketchUpAPI/model/model.h>
#include <SketchUpAPI/model/texture.h>
#include <SketchUpAPI/geometry/point3d.h>
#include "../common/utils.h" // For SU_CALL macro
#include <iostream>
int main() {
// Initialize the API
SUInitialize();
// Create a model
SUModelRef model = SU_INVALID;
if (SUModelCreate(&model) != SU_ERROR_NONE)
return 1;
bool success = true;
SUGeometryInputRef input = SU_INVALID;
// We don't expect exceptions from SLAPI. This try-catch is just a convenient
// way to handle error codes that might be returned from SLAPI, turning them
// into an exception via the SU_CALL macro.
try {
// We'll create a textured face using the SUGeometryInput API.
SU_CALL(SUGeometryInputCreate(&input));
SUPoint3D vertices[9] = { {10.471, 3.52, 3.151},
{6.105, 3.181, 2.997},
{6.058, 3.782, 3.662},
{0, 3.311, 3.662},
{0.258, 0, 0},
{6.316, 0.471, 0},
{8.568, 0.646, 0.079},
{8.529, 1.136, 0.621},
{10.471, 3.52, 3.151} };
SUVector3D offset = { 400000, 5000000, 100 };
for (auto& vert : vertices)
{
// Offset the verticies far into the distance
// this causes the issue with the uv coordinates and the texture rendering
SU_CALL(SUPoint3DOffset(&vert, &offset, &vert));
}
SU_CALL(SUGeometryInputSetVertices(input, 9, vertices));
// Add the first face
SULoopInputRef loop = SU_INVALID;
SU_CALL(SULoopInputCreate(&loop));
SU_CALL(SULoopInputAddVertexIndex(loop, 0));
SU_CALL(SULoopInputAddVertexIndex(loop, 1));
SU_CALL(SULoopInputAddVertexIndex(loop, 2));
SU_CALL(SULoopInputAddVertexIndex(loop, 3));
SU_CALL(SULoopInputAddVertexIndex(loop, 4));
SU_CALL(SULoopInputAddVertexIndex(loop, 5));
SU_CALL(SULoopInputAddVertexIndex(loop, 6));
SU_CALL(SULoopInputAddVertexIndex(loop, 7));
SU_CALL(SULoopInputAddVertexIndex(loop, 8));
size_t face_index0;
SU_CALL(SUGeometryInputAddFace(input, &loop, &face_index0));
// Load texture from file
SUTextureRef tex = SU_INVALID;
SU_CALL(SUTextureCreateFromFile(&tex, "SU_Logo_Color.png", 1.0, 1.0));
// Create material with that texture
SUMaterialRef mat = SU_INVALID;
SU_CALL(SUMaterialCreate(&mat));
SU_CALL(SUMaterialSetTexture(mat, tex));
// Create material input for texture mapping and set it on the faces
SUMaterialInput mat_input;
mat_input.material = mat;
mat_input.num_uv_coords = 3;
SUPoint2D uv_coords[3] = { {0.413446, 0.498106 },
{0.243526, 0.47858 },
{0.238652, 0.513697 } };
size_t uv_vertices[3] = { 0,1,2 };
for (size_t i = 0; i < 3; ++i) {
mat_input.vertex_indices[i] = uv_vertices[i];
mat_input.uv_coords[i] = uv_coords[i];
}
SU_CALL(SUGeometryInputFaceSetFrontMaterial(input, face_index0, &mat_input));
// Fill the root entities of the model using this geometry input
SUEntitiesRef entities = SU_INVALID;
SUModelGetEntities(model, &entities);
SU_CALL(SUEntitiesFill(entities, input, false));
// Save the model
SU_CALL(SUModelSaveToFile(model, "textured_face.skp"));
} catch (std::exception&) {
success = false;
}
// Clean up
SUGeometryInputRelease(&input);
SUModelRelease(&model);
SUTerminate();
if (success) {
std::cout << "File creation successful: textured_face.skp" << std::endl;
} else {
std::cout << "File creation failed." << std::endl;
}
return success ? 0 : 1;
}
SketchUp isn't designed to be used with really large coordinates from the origin.
Typical workflow is to set the geolocation of the model to somewhere around the centre of your geometry, then use functions that convert from local coords to world coords.
In the Ruby API we have model.point_to_utm(point)
and model.utm_to_point(utm)
. There is even the UTM
class: https://ruby.sketchup.com/Geom/UTM.html
However, now that I'm looking, I'm not seeing this exposed to the C API... 🤔
Looks like we have to add those.
I haven't tested this but it's how you resolve this issue in other rending engines...
Create the elements with UVs close to 0,0,0. Then put these into a Group/Component and move to your large coordinate basepoint. Hopefully the UVs will be rendered correctly.