Texture Writer writes out the whole material instead of a small cutout


#1

Hi SU Community,

I ve been having an issue where under some circumstances when I write out the texture of a narrow/thin face, SU will output the whole PhotoMatch material instead. This is consistent on using both the ruby API as well as extracting a .dae.

To reproduce it open the attached skp file, and either extract to Collada, or run the below:

face = Sketchup.active_model.entities.detect{|ent| ent.is_a? Sketchup::Face}
temp_cutout = Tempfile.new(['cutout','.jpg'])
temp_cutout.close

texture_writer = Sketchup.create_texture_writer
texture_writer.load(face, true)
texture_writer.write(face, true, temp_cutout.path)

puts "#{temp_cutout.path} has the full image, instead of a thin vertical segment"

I think this is a bug of SU, but would love to hear what you think.
texture_bug.skp (520.6 KB)


#2

Hi @gpavlidi,
The API is behaving as expected. TextureWriter will always output the full image, regardless of how the texture is mapped on to the face. If you want a narrow strip of the image, you’d need to extract it from the full image using the texture coordinates on the face.


#3

Hi @bugra,
thanks for looking into this.
I dont think the API is behaving as expected, as TextureWriter is supposed to output the undistorted cutout of the face (at least thats what it’s doing for 99% of faces in the models I work with)
In fact if I reproject the PhotoMatch photo in the same narrow face and tweak it with Texture->Position it works fine and the output is a narrow strip. You can try it in the below skp file, running the same ruby code as above.
So I guess something in the mapping of the first skp file is throwing TextureWriter off and makes it output the full image, whereas in the second skp it works as expected (for me at least).

texture_bug2.skp (520.6 KB)


#4

Hmm… yes you’re right. When I have some time, I’ll need to dig into the code and see what’s going on.
I wonder if @tt_su has an insight.


#5

Seems like the post was moved to Ruby API by @Geo
Wouldnt want this to be perceived as an API question, ruby code is there just to reproduce it easily, you can run on the same behavior if you File->Export->Collada .
I think it’s a bug and should be under ‘Technical Problems’, just so it has better chances getting the necessary attention from SU devs too.

Thanks!


#6

That’s almost correct - the exception is when the mapping onto the face is produce UVQ values where the Q value is not 1.0.

Lets have a quick look at the various ways a texture can be mapped on to a face:

All the three first cases will produce UVQ mapping where the Q value is 1.0 - this means the texture mapping plane is planar to the face.

In the last case the texture plane is different from the face’s plane - and you will see values of Q that is not 1.0.

Many file formats doesn’t support the Q value - and that’s where the TextureWriter comes in. When you feed the Texture writer a face where the texture plane is different from the face it will generate a new texture and new UV coordinates. This allows exporters to export SketchUp models which is visually accurate.

(Some exporters take the approach of dividing U and V by Q - but that’s just an approximation and visual deviation will occur. However some exporters prefer this over generating a large amount of unique textures.)

When applying projected textures you often end up with perspective mapping because the source texture mapping normal will be different from the target faces’ normal.

Unfortunately the documentation for the TextureWriter doesn’t explain this behaviour very well. We have an open issue to improve this. But that’s the original intent for it. It has never been intended to always crop and create new unique textures.

The Ruby API Sketchup::TextureWriter class is a thin wrapper on top of our internal C++ TextureWriter class which our exporters use. When you observe that an export - or any usage of TextureWriter - produce a mix of cropped and non-cropped textures, that is intended behaviour. Our exporters prefer to export original texture files - but will fall back to creating unique ones only when needed to preserve visual appearance of the exported mesh.

One common requests we have from developers is to export all textures in a model without cropping. I think this is the first time I’ve heard it’s desired to see the behaviour for all faces.

If you can provide us with an use-case for why you want all textures for all faces to be cropped like this I can file a feature request.


#7

Hi @tt_su,

thanks for the very detailed explanation, it definitely makes more sense. I can confirm that in my case the desired cutouts are generated only for perspective transforms.

Here’s my use case:
Lets say you have a cube, and 4 PhotoMatch photos (8MP), one for each side of the cube. If I texture each of the 4 faces with a photo with a non-perspective mapping, and then export a Collada or kmz, the full 8MP images will be included in the model, when in fact you only need 4 512x512 cutouts for example, raising the filesize significantly. Also if u needed to atlas the cutouts for further optimization, you d need to crop them first from the full size images and adjust uvs to the new size, adding more post-processing.

The ideal thing for my use case would be to add something like a “force_new_textures” flag to Sketchup::TextureWriter that would let you have more control on exported textures etc. and thus size of output model.


#8

@gpavlidi,

have a look at Aerilius’s Make Unique Texture++
does it do what you refer to?
john


#9

Hi @john_drivenupthewall,

it does exactly what I want, to the expense of added complexity + ImageMagick.
Would be great if SU supported this natively.

In any case, that’s very useful, thanks!


#10

[quote=“gpavlidi, post:9, topic:13581”]
In any case, that’s very useful, thanks!
[/quote] my pleasure…

another approach may be to use view.write_image to capture each face [independently], then re-apply each of those as a unique replacement texture…

john


#11

Not sure if this is going to work, as I think this will also capture 3d geometry, axes etc.
I guess I could hide them first, but then i need to figure out the optimal position of the view.camera for each face.

What I ended up doing is, whenever I get a non-perspective transform, I just add 0.001 pixel to one of the uvs. That usually makes it perspective enough so that SU is generating a unique cutout. Pretty ugly hack but havent noticed any significant visual artifact of the +0.001.


#12

Or maybe something like exposing the Make Unique Texture functionality:

?

It’s not likely that the Ruby API itself will offer general image manipulation functionality - that’s rather out of the scope. You’d be better off looking for such libraries either in Ruby - or probably better, C libraries which you can integrate via Ruby C Extensions.


#13

This would work although it would “pollute” Sketchup.active_model.materials adding all these cutouts when exporting. The good thing about TextureWriter is that it only affects a given output file.

I hear you about image manipulation, at least after this thread it’s clear how TextureWriter works so I should be able to crop the cutout myself. Having said that, I would definitely welcome a ‘force_new_textures’ kind of functionality, as it ll result in significantly lighter models in some cases, but until then Image Magick might be the way to go.


#14

That makes sense. Flooding the model with materials wouldn’t be good - and it’s create undo operations.