UV Coordinates Incorrect When Geometry is Far From Origin

Hey there,

We’re working with 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 TextureMapping example included with the SDK to show this issue. We’ve been able to reproduce it with the version we’re currently using 2023.1.315, and with the latest available 2024.0.553.

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. (Screenshot will be posted below)

Now if we offset the geometry quite far from the origin (400 000, 5 000 000, 100) we now see that the texture has become quite disconnected.

Has anyone experienced something like this before, is there something we might be able to do to mitigate this?

If you’d like to attempt this yourself I’ve reposted the TextureMapping sample code, modified slightly to show this behaviour. Line 48 does the offsetting, if it’s commented out everything works as expected around the origin.

Thanks,
Matthew

This is a screenshot of the working case

And here is the modified TextureMapping sample code:

TextureMapping::main.cpp
#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;
}

Why “on Earth” would you want to draw geometry 79 MILES away from the origin?

You could try drawing the face inside the context of a group or component, so it uses local coordinates.

In this case I believe the customer is using the coordinates of their source data which happens to be UTM-32N, so coordinates in this range would put them roughly within continental Europe.

A group or component sounds interesting, I’ll need to look into that. I wonder if the face is constructed using local coordinates within the group/component and then the entire group/component is offset would we still see the texture issues… I’ll have to do some testing.

So, perhaps the customer must believe (mistakenly) that the model origin should be set to the UTM datum in use?

OR is the model itself 79 miles in one of it’s dimensions ?

  • What I’m getting at here is that SketchUp is notorious for quirky behavior when geometry is far from the origin. This may happen because internally it’s APIs use inches as the unit of measure.

In order to position SketchUp models in a world model (such as Google Earth) the origin of the SketchUp model can be geolocated and it’s north angle set (to rotate the Y axis off of world north.)

See:


Also note that the “live” SketchUp Ruby API has a UTM class and get_datum, list_datums and set_datum methods. These are lacking in the C API, but I had previously opened a parity issue for this:

1 Like