Monday, September 29, 2008

Reading Textures from a SketchUp Model Using the Reader SDK



In case you haven't looked yet, we have an SDK that allows you to read SketchUp files using a C++ interface. This allows you to write your own exporter for Google SketchUp or an importer for another application to read SketchUp files. Oftentimes you want all the polygons on your exported model to be triangles. Since most faces in a SketchUp model are quadrilateral polygons, you will need to triangulate the faces. Also, if a face has a texture you will want the uvq coordinates of the triangulated faces as well. Besides the face and the mesh that represents the face after it is triangulated, there are several other objects in the SketchUp Reader SDK that are used to read the uvq texture coordinates of a face. For example, there is the texture writer object. This is the object used to write out the face textures to disk. In order to retrieve the uvq texture coordinates of the mesh you also need a UV helper object and also a perspective object.
First, using the SkechUp document object, create the texture writer object:


ISkpApplication* sketchup_application = NULL;
sketchup_document->get_Application(&sketchup_application);
ISkpTextureWriter* texture_writer = NULL;
sketchup_application->CreateTextureWriter(&texture_writer);

Next, retrieve the ISkpCorrectPerspective interface of the texture writer:


ISkpCorrectPerspective* perspective = NULL;
texture_writer->QueryInterface(IID_ISkpCorrectivePerspective,
                               &perspective);


For each face retrieve its UV helper object using the perspective object:


ISkpUVHelper* uv_helper = NULL;
face->GetUVHelper(face,
                  true, // front face
                  perspective,
                  &uv_helper);


Finally, create the triangulated mesh:


ISkpPolygonMesh* mesh = NULL;
face->CreateMesh(
     PolygonMeshPoints |
     PolygonMeshUVQFront |
     PolygonMeshUVQBack,
     perspective, &mesh);


Index through the vertices of the mesh and retrieve the position value of each vertex. If the face object belongs to a group or a component instance, the position needs to be transformed. The homogeneous transformation used to transform the position is the aggregate of the transformation of the parent group or component instance. If the parent is nested in other group(s) or component instance(s) then you the need to aggregate all the transformations.


double position[] = {0, 0, 0};
mesh->_GetPoint(index, position);


Finally, retrieve the uvq value that corresponds to the vertex position. The uvq value does not need to be transformed like the position value:


double uvq[] = {0, 0, 0};
uv_helper->GetFrontUVQ(
     position[0],
     position[1],
     position[2],
     &uvq[0],
     &uvq[1],
     &uvq[1]);


Stay tuned for further tips and tricks.


16 comments:

Ken Schafer said...

Thanks for the tip, but I don't understand what the ISkpCorrectPerspective* object is doing. I get no difference in the UVS or CreateMesh regardless as to whether I pass a perspective object or NULL. Could you explain why I want to go through the "effort" to create one?

snorkel said...

If only it worked as easily as that. It appears that the units the uvs come in depend on whether the face has its own material, or inherits a material from a parent component or group. The scaling on the uvs change depending on which case. Also, it's not clear to me whether I need to write (u,v), ((u/q),(v/q)), or even (u*xscale,v*yscale), or indeed (u*xscale/q,v*yscale/q). Results of my export vary on how the textures were applied and I'm struggling to find a way to export the right data for all cases.

Big Perm Racing and Auto Detailing said...
This comment has been removed by the author.
Sang Ahn said...

If you want to apply texture to a face that is part of a component, you will want to use GetUVHelperWithTextureHandle to retrieve the uv helper, using the handle of the parent component instance. Textures read through the texture writer are linearly interpolated, therefore if the q value is used at all, it is used as a scale factor.

silent said...

Hello! I've got interesting behaviour while using ISkpUVHelper with subcomponents... Let me describe.

There is a component in scene contains a face and other component (this subcomponent contains, for example, two faces). I'm applying material to a top-level component and it is distributed to all the faces in components geometry (they all haves "default" material).

I'm surely using ISkpFace::GetUVHelperWithTextureHandle() and ISkpTextureWriter2::LoadComponentInstance() to get handles. Such way I receive handles for both top-level and sub- components, they differs.

So when I loading faces of subcomponent I'm using corresponding texture handle, but it results wrong texture coordinates. For top-level component's face it is ok (with it's handle).

Most interesting is that when I use texture handle of top-level component for subcomponent UVHelper, it works correctly.

I also have to notice the subcomponent doesn't have applied texture.

So does it mean I should check if there's no material and use the parent's component texture handle for child's faces?..

Thanks a lot!

// SketchUp v6.4.112

Deelip Menezes said...

I downloaded the SketchUp C++ SDK today and could not find information on how to develop a SketchUp plug-in DLL. The SDK documentation mentions a few things, but not nearly enough to actually build a plug-in from scratch. Do you happen to have the C++ source code of a sample SketchUp plugin that you can share. If yes, please send me something at deelip (at) sycode (dot) com

Anonymous said...

Hi,

I can't get this code from the blog to compile. Can anyone help?

ISkpCorrectPerspective* perspective = NULL;
texture_writer->QueryInterface(IID_ISkpCorrectivePerspective, &perspective);

I've also tried:

ISkpCorrectPerspective* perspective = NULL;
texture_writer->QueryInterface(IID_ISkpCorrectPerspective, &perspective);

Thanks

Sang Ahn said...

This should work
texture_writer->QueryInterface(IID_ISkpCorrectPerspective, (void**)&perspective);

Steve said...

>> This should work
texture_writer->QueryInterface(IID_ISkpCorrectPerspective, (void**)&perspective);

I get the following error:

"error C2065: 'IID_ISkpCorrectPerspective' : undeclared identifier"

I'm using VC++ 2005.

Sang Ahn said...

Does your file include sketchupinterfaces.h ?

Steve said...

>> Does your file include sketchupinterfaces.h ?

It doesn't. I guess this is the problem. Where can I find this?

Many thanks.

PS: Any general tips for using the SDK are more than welcome. :)

Steve said...

>> Where can I find this?
All sorted. I found sketchupinterfaces.h in sdk\SkpToXMLExample\SketchUp\, and a few other directoris.

Steve said...

Sigh :(

I now get a linker error.

error LNK2001: unresolved external symbol "struct _GUID const IID_ISkpCorrectPerspective" (?IID_ISkpCorrectPerspective@@3U_GUID@@B)

Can anyone tell me exactly what files I need to have in my VC++ project to allow me to create a triangulated mesh from a skp face?

Does anyone know any forums where the skp C++ SDK is discussed?

Many thanks

silent said...

Steve, I think you should also include a "SketchUp_i.c" file where those IID_### defined. You may find this file in SDK.

FlagFreak said...

This is going to be a great blog! Thanks for the update after so many long months. :-D Can't wait to read more from you guys! ;-)

Anonymous said...

I looked at the SketchUp API and IT WAS DISAPPOINTING. It's the usual deal: no overview, just a listing of calls with one line descriptions (If you are lucky) and a few poorly documented example programs that don't do anything useful. It's like whoever did them got bored after half an hour and figured "you can sort it out yoursef". Before you code anything with that SDK, you'll have to waste a lot of time reverse engineering what Google were too damned lazy to write down. I'd rank this down there with the original version of DirectX for lazy documentation.

Poor Effort, Google!