Sphere perfect texturing. Here’s some samples. [Committed]

Since I’m heavily using spheres for my games (suns, planets, moons), there are some issues with the way those are textured.



Sphere.TextureMode.Original will wrap texture radially and along z-axis.

Sphere.TextureMode.Projected will wrap texure radially, but spherically project along z-axis.

Sphere.TextureMode.Polar will apply texture to each pole. Eliminates polar distortion, but mirrors the texture across the equator.



Those three, although working, still have some lacks. There will be distortion in the case of the first two, and Polar will introduce mirroring “issues”.



Lastly, Original and Projected wrap the texture around the Z axis. To have the texture applied so the poles are up and down (on the Y axis), you have to rotate the geometry.



A couple of days ago I found a great article on how to take a texture and deform it so that it would effectively fit a sphere with very minimal deformation. It’s actually unnoticeable to the eye.



Here’s what the code does to the texture once it’s done its thing:



For this example I used a 800x800 grid so it’s easy to see.

This is the original image:



This is the “SphereMapped” image:





The only drawback to this is with the texture size compared to the sphere size. But this could easily be remedied by using a higher resolution image.



What is missing in jME’s case is, what I would call a, Sphere.TextureMode.Basic mode. Essentially, it would simply map the texture the way it is without trying to correct it, but it would have to wrap it around the Y axis so no rotation has to be done on the geometry.

After having skimmed the setGeometryData() method in Sphere I am ambiguous on if the Original TextureMode does some “recalculation” to fix the pixels or not. I don’t think it is, but at first glance it’s hard to see. If it’s not then having it apply the texture on the Y axis would be ideal.



I’d like to share that code with all of you and to do so I’d like to make some kind of “plugin” but I have no idea where to start. The whole thing is actually pretty small and simple. I’m currently browsing the SDK wiki on how to do that, but it’ll take some time before I can find the way to properly do this. Also having no experience at all using AWT? or whatever it’s call for GUI in jMP, it’ll take some time. Anyway, if one of you knows how to do this fast and would like to point me in the right direction so I can push this out faster, I’ll gladly take all the help I can get. :smiley:

4 Likes

Looks cool. There is actually a texture editor with a plugin system already in jMP, but its no longer maintained by its author… Maybe you can simply check out the source of the SDK and look into that, maybe it saves you from having to do anything but applying your code to the image. I don’t know how well finished the plugin system is but the writer usually wrote nice and clean API. I guess you can create a new “filter”, I can add the code to the TextureEditor or give you commit access then.



Edit: Link

http://code.google.com/p/jmonkeyengine/source/browse/#svn%2Ftrunk%2Fsdk%2Fjme3-texture-editor%2Fsrc%2Fcom%2Fjme3%2Fgde%2Ftextureeditor%2Ffilters

Here’s the result used with Sphere.TextureMode.Original without the corrected image then with the correction applied to the texture. Note that the texture isn’t tileable but you can clearly see the difference.



So as I suspected, that Original mode is working as intended without modifying pixel location. I think we all can live with rotating the geometry. It’s not a big deal really.



@normen, I’ll take a look at that. Btw, I’m wondering if simply adding that mode inside Sphere.java wouldn’t be preferable. Maybe even replace Projected?



Well, that was mostly painless. :smiley:



@normen, I’ve successfully imported the Texture Editor module and added the filter. I tested the filter and it works as it should. If you want I can commit the changes (hopefully I haven’t broken anything) or if you prefer I can send you the whole jar + the nbm if needed.

That’s a very nice piece of work madjack!!

madjack said:
I've successfully imported the Texture Editor module and added the filter. I tested the filter and it works as it should. If you want I can commit the changes (hopefully I haven't broken anything) or if you prefer I can send you the whole jar + the nbm if needed.

You can commit if you want, you can also post the filter here if you like that better. The jar or nbm wouldn't be much help.

Getting a 403 Forbidden error when trying to commit the Texture Editor:



==[IDE]== Jun 20, 2011 9:56:18 PM Importing...
svn: URL 'https://jmonkeyplatform-contributions.googlecode.com/svn/trunk/textureeditor' non-existent in revision '392'
svn: URL 'https://jmonkeyplatform-contributions.googlecode.com/svn/trunk/textureeditor' non-existent in revision '392'
import -m "- Added the SphereMapped texture modification...." -N H:/Users/Dany/AppData/Local/Temp/svn_dummy/textureeditor https://jmonkeyplatform-contributions.googlecode.com/svn/trunk/textureeditor
svn: MKACTIVITY of '/svn/!svn/act/94a3e9af-3001-0010-a5e4-537dadbba38b': 403 Forbidden (https://jmonkeyplatform-contributions.googlecode.com)
svn: MKACTIVITY of '/svn/!svn/act/94a3e9af-3001-0010-a5e4-537dadbba38b': 403 Forbidden (https://jmonkeyplatform-contributions.googlecode.com)
==[IDE]== Jun 20, 2011 9:56:27 PM Importing... finished.


I followed the instructions found here at the bottom of the page.

Gah.



I think I got it right this time. It seems the way I had it done the last time was wrong (the plugin name was wrong). It seems to be ok now but I’ve got a different error.


svn: Commit failed (details follow):
svn: MKACTIVITY of '/svn/!svn/act/661449b0-3001-0010-b197-97a72853cad8': 405 Method Not Allowed (http://jmonkeyengine.googlecode.com)
svn: MKACTIVITY request failed on '/svn/!svn/act/661449b0-3001-0010-b197-97a72853cad8'


What is it I am doing wrong? :/
nehon said:
That's a very nice piece of work madjack!!

I can't take that credit. I haven't done anything really. It was mostly cut/paste and added a couple of things to make it work in java. The result is quite nice though. :D
madjack said:
It was mostly cut/paste and added a couple of things to make it work in java.

Yeah....what do you think I do? :p

I tried everything I could think of and it doesn’t work so here’s the diff for ImageEditorComponent.java and the new filter file.



Enjoy.



[patch]

— Base (BASE)

+++ Locally Modified (Based On LOCAL)

@@ -39,6 +39,7 @@

import com.jme3.gde.textureeditor.filters.MirrorFilter;

import com.jme3.gde.textureeditor.filters.ResizeFilter;

import com.jme3.gde.textureeditor.filters.RotateLeftFilter;

+import com.jme3.gde.textureeditor.filters.SphereMappedFilter;

import com.jme3.gde.textureeditor.tools.ColorPicker;

import com.jme3.gde.textureeditor.tools.CropTool;

import java.io.IOException;

@@ -169,6 +170,7 @@

scroller.setViewportView(imageContainer);

}


  • @SuppressWarnings("unchecked")

    private void createToolBar() {

    final JButton zoomIn = new JButton(Icon("zoom-in-2.png"));

    final JButton zoomOut = new JButton(Icon("zoom-out-2.png"));

    @@ -259,8 +261,8 @@

    builder.addFileFilter(FileFilters.TGA);

    builder.addFileFilter(FileFilters.PNG);


  •    JFileChooser fc= builder.createFileChooser();<br />
    
  •    fc.setFileSelectionMode(fc.SAVE_DIALOG);<br />
    
  •    JFileChooser fc = builder.createFileChooser();<br />
    
  •    fc.setFileSelectionMode(JFileChooser.SAVE_DIALOG);<br />
    

fc.setAcceptAllFileFilterUsed(false);



int a = fc.showOpenDialog(COMPONENT);

@@ -275,7 +277,7 @@

} else if (name.endsWith(".bmp")) {

type = "bmp";

} else if (name.endsWith(".tga")) {

  •            type =&quot;tga&quot;;<br />
    
  •            type = &quot;tga&quot;;<br />
    

} else {

ExtensionFileFilter filter = (ExtensionFileFilter) fc.getFileFilter();

file = new File(file.getParentFile(), file.getName() + filter.getExtension());

@@ -351,6 +353,7 @@



final JMenuItem gray = filters.add("Grayscale");

final JMenuItem bright = filters.add("Brightness");

  •    final JMenuItem sphereMap = filters.add(&quot;SphereMapped&quot;);<br />
    

ActionListener al = new ActionListener() {

@@ -366,11 +369,13 @@
spawnEditor(GrayscaleFilter.create().filter(editedImage));
} else if (source == bright) {
spawnEditor(BrightFilter.create().filter(editedImage, ImageEditorComponent.this));
+ } else if (source == sphereMap) {
+ spawnEditor(SphereMappedFilter.create().filter(editedImage));
}
}
};

- for (AbstractButton b : Arrays.asList(bumpSoft, bumpMedium, bumpStrong, gray, bright)) {
+ for (AbstractButton b : Arrays.asList(bumpSoft, bumpMedium, bumpStrong, gray, bright, sphereMap)) {
b.addActionListener(al);
}
}
[/patch]

Now the SphereMappedFilter.java
[java]
package com.jme3.gde.textureeditor.filters;

import java.awt.image.BufferedImage;

/**
* Texture Mapper
*
* This class will take a square texture and will map it for a spherical
* adaptation so no, or at least no visible, distortion is apparent.
*
* @author MadJack
* @created 06/18/2011
*/
public class SphereMappedFilter implements BufferedImageFilter {

public static SphereMappedFilter create() {
return new SphereMappedFilter();
}

protected SphereMappedFilter() {
}

/*
*
* The following algorithm is based (more like copied) from
* Paul Bourke's pseudo code available at:
* http://paulbourke.net/texture_colour/texturemap/
*
*
*/
@Override
public BufferedImage filter(BufferedImage sourceImg, Object... args) {

BufferedImage imageOut = new BufferedImage(sourceImg.getWidth(), sourceImg.getHeight(), BufferedImage.TYPE_INT_ARGB);

double theta, phi, phi2;
int i, i2, j;

// Read an input image from a file
for (j = 0; j < sourceImg.getHeight(); j++) {
theta = Math.PI * (j - (sourceImg.getHeight() - 1) / 2.0f) / (sourceImg.getHeight() - 1);
for (i = 0; i < sourceImg.getWidth(); i++) {
phi = Math.PI * 2 * (i - sourceImg.getWidth() / 2.0f) / sourceImg.getWidth();
phi2 = phi * Math.cos(theta);
i2 = (int) Math.round(phi2 * sourceImg.getWidth() / (Math.PI * 2) + sourceImg.getWidth() / 2);

int newpixel = 0;
if (i2 < 0 || i2 > sourceImg.getWidth() - 1) {
/* Should not happen, make that a red pixel*/
newpixel = 100;
} else {
newpixel = sourceImg.getRGB(i2, j);
}
imageOut.setRGB(i, j, newpixel);
}
}
return imageOut;
}
}
[/java]
madjack said:
What is it I am doing wrong? :/

The problem is that you can only commit when you checked out via https, additionally you have to be added to the projects commiters list ;)
normen said:
The problem is that you can only commit when you checked out via https,

Ah, didn't know and it wasn't mentioned in the wiki but it's understandable that it's not.


additionally you have to be added to the projects commiters list ;)

Makes me wonder why you suggested I commit it then. :P

Oh well. It doesn't matter anyhow. It'll soon be available for everyone to use if they want to.

BTW, there are many things that could be added and/or fixed in the texture editor. I might have a bit of fun with it. :D
madjack said:
Makes me wonder why you suggested I commit it then. :P

Well for one I didn't check back the committers list and then its no problem I add you, just PM me your googlecode mail.

Changes have been committed.



As far as I know there are no issues with the code. I haven’t tested the result of a SphereMapped texture that isn’t square on a sphere so I can’t say if that would work or not. I think technically it should.



Anyway, let me know if there are some problems with the plugin.

1 Like

Well done :wink:

Thanks for your commit. Just a question: Is it possible to get a perfect uv unwrapped sphere? I mean obj with uv coordinates.



My purpose is to texturize sphere sky. To make a sphere texture instead of a skybox texture. Thanks.

nehon said:
Well done ;)

I take it you tested it?

@mifth
This wouldn't work because the modification is for an external wrapping and afaik this wouldn't give the same result. Although if you applied that texture to a 3D sphere in a 3D program and using flat texturing, I guess it would work. As you can see, I haven't tested it that way. I know it works on a 3D sphere inside jME, but that's the extent of my testing.

There are some tutorials for doing a sky dome around the web using 6 sky box textures and converting them in a sky dome. The one I have bookmarked I haven't tested out but you can check it out and see if that does the job for you.
Making Sky Dome from Sky Box.

Hopefully that'll help you out.

I know about the skybox, but it would be easy to texturize sphere. Sphere mapping is supported for JME sky. Ok, i’ll test my own sphere and post it here. Thanks.

It seems I misunderstood you question.



I can’t tell you if it’s possible to get a square (I imagine that’s what you’re asking) texture and UV map it perfectly to a sphere. I have the bare necessities in 3D knowledge so I would be the wrong person to ask that.



Sorry.