Cannot get the file paths for textures on 32-bit


#1

I’m making an importer using the SketchUp SDK, and I’m having problems to import textures. I use SUTextureWriterWriteAllTextures() to write them to disk, and then SUTextureWriterGetTextureFilePath() to get the paths to the files.

The problem is that SUTextureWriterGetTextureFilePath() always returns the error code SU_ERROR_NULL_POINTER_OUTPUT, which according to the documentation is returned when the SUStringRef pointer passed to the function to receive the path is null, but it’s not the case here. But I’m only having the problem when building a 32-bit application, when building a 64-bit one it works fine; I use the correct DLLs for each one, and anyway everything seems to work fine when not loading textures with both versions. The textures are indeed being written to disk correctly, and it happens with all the SketchUp files with textures I have tried.

I’m using the SDK inside a C# program, in case that mattered, though it shouldn’t.

Is there any bug in the 32-bit version of SUTextureWriterGetTextureFilePath()?

Thanks.


#2

Hi

I tried to reproduce this, without success. Maybe you can provide sample snippet and model?

I’m attaching the snippet and model I used.
(Visual Studio 2013, SketchUp 2016 C SDK)
Build and runs on both Win32 and Win64 configs for me.

// SLAPI-Test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <assert.h>
#include <iostream>
#include <string>
#include <vector>

#include "SketchUpAPI/sketchup.h"


int _tmain(int argc, _TCHAR* argv[])
{
  std::string project_path = "C:\\Users\\tthomas2\\SourceTree\\SLAPI-Test";
  std::string model_path = project_path + "\\models\\GetTextureFilePath.skp";
  std::string temp_path = project_path + "\\temp";

  SUInitialize();

  SUModelRef model = SU_INVALID;
  SUResult result = SUModelCreateFromFile(&model, model_path.c_str());
  assert(result == SU_ERROR_NONE);

  SUEntitiesRef entities = SU_INVALID;
  result = SUModelGetEntities(model, &entities);
  assert(result == SU_ERROR_NONE);

  SUTextureWriterRef tw = SU_INVALID;
  result = SUTextureWriterCreate(&tw);
  assert(result == SU_ERROR_NONE);

  size_t num_faces = 0;
  result = SUEntitiesGetNumFaces(entities, &num_faces);
  assert(result == SU_ERROR_NONE);

  std::vector<SUFaceRef> faces(num_faces, SU_INVALID);
  size_t num_faces_out = 0;
  result = SUEntitiesGetFaces(entities, num_faces, &faces[0], &num_faces_out);
  assert(result == SU_ERROR_NONE);

  std::vector<long> texture_ids;
  for (size_t i = 0; i < num_faces_out; ++i)
  {
    auto face = faces[i];
    long front_id = 0;
    long back_id = 0;
    result = SUTextureWriterLoadFace(tw, face, &front_id, &back_id);
    assert(result == SU_ERROR_NONE || result == SU_ERROR_GENERIC);
    if (result == SU_ERROR_NONE)
    {
      texture_ids.push_back(front_id);
      texture_ids.push_back(back_id);
    }
  }

  result = SUTextureWriterWriteAllTextures(tw, temp_path.c_str());
  assert(result == SU_ERROR_NONE);

  for (const auto& texture_id : texture_ids)
  {
    SUStringRef path = SU_INVALID;
    SUStringCreate(&path);
    result = SUTextureWriterGetTextureFilePath(tw, texture_id, &path);
    assert(result == SU_ERROR_NONE || result == SU_ERROR_NO_DATA);

    if (result == SU_ERROR_NONE)
    {
      size_t size = 0;
      result = SUStringGetUTF8Length(path, &size);
      assert(result == SU_ERROR_NONE);
      ++size; // For NULL termination.

      size_t size_out = 0;
      char* path_utf8 = new char[size];
      result = SUStringGetUTF8(path, size, path_utf8, &size_out);
      assert(result == SU_ERROR_NONE);

      std::cout << "Texture Id: " << texture_id << std::endl;
      std::cout << "      Path: " << path_utf8 << std::endl;

      delete[] path_utf8;
    }

    result = SUStringRelease(&path);
    assert(result == SU_ERROR_NONE);
  }

  SUTextureWriterRelease(&tw);
  SUModelRelease(&model);

  SUTerminate();
  return 0;
}

GetTextureFilePath.skp (107.0 KB)


#3

That being said - there are two ways to export textures from the model:

  1. SUTextureWriterRef
    SketchUp uses UVQ data - not just UV. The Q value is used when the texture mapping isn’t affine - you often get this when you project textures onto faces. Many exporters and file formats doesn’t handle this - they expect only UV data. The Texture Writer will take these non-affine mapped faces and generate new textures and “normalize” the UV coordinates. If this is what you expect then you should continue using it.

  2. SUTextureRef
    In many cases we see people using the Texture Writer when they really only want to export all textures in a model - without any modifications. And the texture writer require you to find all the entities which have textures applied. But to simply export all textures it’s possible to simply iterate all materials in a model and call SUTextureWriteToFile.

The exact nature of the Texture Writer is something we need to clarify. I realize the docs doesn’t do a good job for this utility.


#4

Thanks for the response. Your example worked fine, so after a good while banging my head against the wall, I found the problem.

As I said, I’m using the library from C#, which by itself is not a problem, but when it came the time to use the texture identifiers, which are long in C/C++, I just used long as well on the C# part… buuuuut, despite the names, they are not the same types: in C# the type is always 64-bit, while in C it’s process-dependent; so passing a 64-bit argument to a function expecting a 32-bit one, probably caused an overflow of 4 bytes that got into file_path as 0. Indeed, now that I have changed the type to System.IntPtr, it’s working perfectly on both 32 and 64 bits.

So one of those stupid things that happen from time to time in the life of a programmer, I’m sorry :flushed:

About the different ways to export textures, yes, in my case SUTextureWriterRef is the function that I want to use, since the importer is for a game engine that works with UV coordinates.

As an aside, I think the docs need a bit more explanation in some parts. In my case, it took me a bit of time to realize that apart from the “root” geometry, there are also “groups” and “component instances” with geometry, something I haven’t seen explicitly mentioned and that doesn’t seem to be in the examples (as far as I know).

Thanks.


#5

Yea, long is troublesome in C/C++ - you cannot rely on its size. I tend to avoid it whenever possible. It bothers me somewhat that the C API used that for the texture ids.

Thanks for the feedback. (@ChrisFullmer: another case for our list of things to improve.)