SketchUp / api-issue-tracker

Public issue tracker for the SketchUp and LayOut's APIs
https://developer.sketchup.com/
38 stars 10 forks source link

UV Coordinates Incorrect When Geometry is Far From Origin #985

Open mdeabreuatsafe opened 5 months ago

mdeabreuatsafe commented 5 months ago

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.

image

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.

image

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;
}
thomthom commented 4 months ago

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.

thomthom commented 4 months ago

Related: #828

j-te commented 1 month ago

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.