Pages: [1]
  Print  
Author Topic: My new sky initiative :p  (Read 956 times)
cheb
Lesser Nub


Cakes 2
Posts: 119



WWW
« on: April 19, 2018, 03:13:20 am »

Hi, guys  punched
I was working on a mod for [some proprietary game] and it made me realize how human-friendly spherical projection is when working with skyboxes. Unless the angle is too up or down (in the range of +/-30 degrees it's fine I'd say) moving hills and mountains in The GIMP up and down, left and right is very easy and convenient.

So. Keeping in mind the appaling lack of GPL tools to work with cube maps, and corresponding dearth of OA skies, I began working on what I call a skybox composer. I wanted to present a finished product here, but it is slow going - involves tons ov trigonometry which I am a total noob at. Enough to say, I liked math back in school but my grade was always around 3..3+ (C). I have very goood intuitive understanding, but formulae... Argh.

It would be a small but powerful thing, taking a text definition file and a bunch of images and composing them into a sphere or cube map. A bit like command-line Photoshop, if you like. There would be layers, layer groups with blend modes, layer masks and all that jazz. All in about two thousand lines of source code.

The application (besides making skies for [that other game]) would allow for easily maintained and modified skyboxes. For instance, you'd be able to put planets, nebulae or other sprites at arbitrary angles, scales and rotations - and they would be projected correctly. And it keeps the separation of sources (images) and compiled result (the output cubemap) for ease of future modifications!

The license is LGPL as dictated by the image library I use (which is licensed under MPL 1.1 / alternatively LGPL). The application would have a GUI version (for easier re-run with one click) and, maybe, built-in OpenGL viewer of results (later), as well as a command-line version for both Windows and Linux (and, possibly, arm/Raspberry Pi 3). Cool features would include selective Gaussian blur in an up to 270-degree cone (leave a last will to your great-grandkids to see it completing its run).

I hope I'd be able to present an unfinished barebones prototype in a few days. The principles are really too easy. But the math! Argh.  Vector this, matrix that, multiply them how... Angry

Logged

Imma lazy dreamer. I achieved nothing.
fromhell
Administrator
GET A LIFE!
**********

Cakes 31
Posts: 14480



WWW
« Reply #1 on: April 19, 2018, 07:11:45 pm »

My current skybox-making technique is this in Blender:

- make a cube and uvmap that cube to the 6 skybox textures
- make a duplicate of that cube
- subdivide the cube
- spherify the cube in a shape key
- make another UV layer on your "cube" (read: it looks like a sphere at this point). Uvmap out some hemispheres on a new single texture
- paint on new single texture
- refresh your skybox images by 0'ing the shape key and rendering the sphere cube to the cube you made before
Logged

asking when OA3 will be done won't get OA3 done.
Progress of OA3 currently occurs behind closed doors alone

I do not provide technical support either.

new code development on github
cheb
Lesser Nub


Cakes 2
Posts: 119



WWW
« Reply #2 on: April 20, 2018, 02:04:54 am »

Aha! So it *is* possible!  Smiley

But... B̵̖͍̻̗͚̺l̴̼̲̦͍͙̖e̴̢̮̻͎̺͜n̵͈̮͎͖͔͜d̷̘̥͓̘͖͕e̷̡̤̫͔̻̗ŗ̶̝͚͔͇̺! [shudders] Sad The interface made by Chthulhu in Hell! I was as afraid touching it 10 years ago as I am now. I only managed to make a single sphere-mapped sphere md3 by following a step-by-step tutorial on YouTube, and only because time was essential. Because learning the md3 format specification, then coding a sphere generator that generated the required md3 field-by-field would be more intuitive and less taxing on my sanity.  grouch

I stand by my project because anything that lets people do things while avoiding Blender is a must. It's not about choice, it's about saving the innocent minds, bro!
« Last Edit: April 20, 2018, 02:07:57 am by cheb » Logged

Imma lazy dreamer. I achieved nothing.
Gig
In the year 3000
***

Cakes 48
Posts: 4256


WWW
« Reply #3 on: April 20, 2018, 04:19:04 am »

it's about saving the innocent minds, bro!
If you are talking to Fromhell, I think you should say "sis". ^_^
Logged

I never want to be aggressive, offensive or ironic with my posts. If you find something offending in my posts, read them again searching for a different mood there. If you still see something bad with them, please ask me infos. I can be wrong at times, but I never want to upset anyone.
cheb
Lesser Nub


Cakes 2
Posts: 119



WWW
« Reply #4 on: April 20, 2018, 11:59:53 am »

Yea. Embarrassed

Anyways, almost made it work, copied a picture 1:1 (with R and B swapped - why, oh God, why) and then began crashing on each run with weird error messages. Im worn out, would continue tomorrow. punched

P.S. A case of delayed shooting myself in the foot.
ImagingJpeg.pas had following conditionals:
Code:
{$IF Defined(FPC) and Defined(PASJPEG)}
  { When using FPC's pasjpeg in FPC the channel order is BGR instead of RGB}
  {$DEFINE RGBSWAPPED}
{$IFEND}   
where {$DEFINE RGBSWAPPED} was commented out  grouch
So *of course* it was loading jpegs - and only jpegs - with r and b swapped  Angry
I have a sinking suspicion I know the idiot who commented that out. I see him every day in the mirror.  Lips Sealed
« Last Edit: April 20, 2018, 06:35:20 pm by cheb » Logged

Imma lazy dreamer. I achieved nothing.
cheb
Lesser Nub


Cakes 2
Posts: 119



WWW
« Reply #5 on: April 21, 2018, 12:26:05 pm »

..almost... there... [falls down exhausted]  sick

Basic functionality *does* work now, only works with sphere maps, supporting all layer blend modes. See tomorrow: actual useful functionality, like output to cubemaps, layer types for actual planting sprites of arbitrary size and orientation into the sphere, layer types for functions like curves, blur/unsharp mask, constant color layers, et cetera.

Here: combining two arbitrary photos treating them like they were sphere maps.

Quote
[sky]
input_path=
output_path=
output_image_format=png
layers=2,1
height=512
#sphere_width=512

[1]
type=sphere_map
bitmap=000_0178.jpg

[2]
type=sphere_map
mode=soft_light
bitmap=000_0600.JPG
opacity=0.5
roll=-10
yaw=30
pitch=25
Logged

Imma lazy dreamer. I achieved nothing.
cheb
Lesser Nub


Cakes 2
Posts: 119



WWW
« Reply #6 on: April 22, 2018, 10:10:00 am »

Groan. I spent so much time debugging. It drank so much of my blood  grouch

All because I mistyped *twice* when implementing this rotation matrix in my code:
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml

And it *almost* worked. Most of the time. Until I applied the roll angle.

I'm spent.  sick

« Last Edit: April 22, 2018, 10:20:06 am by cheb » Logged

Imma lazy dreamer. I achieved nothing.
Gig
In the year 3000
***

Cakes 48
Posts: 4256


WWW
« Reply #7 on: April 22, 2018, 12:51:37 pm »

Looking forward for seeing a map of yours!  Smiley
Logged

I never want to be aggressive, offensive or ironic with my posts. If you find something offending in my posts, read them again searching for a different mood there. If you still see something bad with them, please ask me infos. I can be wrong at times, but I never want to upset anyone.
cheb
Lesser Nub


Cakes 2
Posts: 119



WWW
« Reply #8 on: April 24, 2018, 09:23:01 am »

Oh, I will!


Probably next week, as making skies for [that other game] is urgent.
For now, implemented: q3 compatible cube map output (but not reading!), layer groups, solid color layers  ccool

For the future... I think, why not *distortion* layer blending modes, like lens effects? Because it all works in 3d vector space anyway.
Also, I haven't implemented layer masks yet.

Note the bottommost sprite, wrapped more than halfway around the sky. The max angle for a square sprite is 254 degrees!  giggity


P.S. Also forgot: the option to mirror the sprite or cubemap; the option to force non-native aspect ratio on a sprite
« Last Edit: April 24, 2018, 09:28:04 am by cheb » Logged

Imma lazy dreamer. I achieved nothing.
cheb
Lesser Nub


Cakes 2
Posts: 119



WWW
« Reply #9 on: May 13, 2018, 04:09:59 am »

Nothing useful for OA - yet - but I finally presernt you the tool:
http://chebmaster.com/downloads/cheskymp.zip
THE TOOL, MAN! Cool

Full source code (LGPL) and usage instruction (readme.txt) inside.
I will start making skies for OA at the same time as I create a dedicated website for this program.

This week I overcame a significant hurdle: GZDoom sky export.
This format is grossly inefficient, stretched non-linearly in a tricky pattern: while the texture has to be 1024 x 512 to avoid auto-repeating, the horizon line is only at 207 pixels from its top.
Trial and error, guys, trial and error. Sniff.

The warped parody of a sphere map GZDoom requires:



The sphere map it was made from (see how smoth and even it is!):
(warning: not a free image, generated by proprietary software tool!)


Sanity check:




Code:
 procedure TOutDoomSkyLayer.BitmapToProjectVector(const x, y: float; out vector: TVector3f);
  var
    pitch, ynl, nlf, yaw, ax, az: float;
  begin
    yaw:= (0 - (x / (image.width - 1))) * 2 * Pi;
    // this shit is *grossly* non-linear!!!
    // top part of sky is half of 415 pixels, with 20 degrees in zenith lost,
    //  the game engine fills it with blurred color derived from the top image edge.
    if y < GZDoomSkyMiddle  then begin
        ynl:= (GZDoomSkyMiddle - y) * (GZDoomSkyTopPart / (GZDoomSkyMiddle * 2));
        //it is stretched non-linearly! The middle is larger.
        nlf:= 1 + (0.5 - ynl / GZDoomSkyTopPart)  * 0.5 * 2;
        ynl*= nlf;
    end
    // nadir. The same story here, blurred color derived from the bottom image edge
    else begin
      ynl := - (y - GZDoomSkyMiddle) * (GZDoomSkyBottomPart / ((GZDoomSkyHeight - 1 - GZDoomSkyMiddle) * 2));
      nlf:= 1 + (0.5 + ynl / GZDoomSkyBottomPart)  * 0.78 * 2;
      ynl*= nlf;
    end;
    pitch:= ynl * Pi;
    vector[0]:= cos(pitch) * sin(yaw);
    vector[1]:= sin(pitch);
    vector[2]:= cos(pitch) * - cos(yaw);
  end;


For comparison, the same code for sane output formats:
Code:
 procedure TOutSphereMapLayer.BitmapToProjectVector(const x, y: float; out vector: TVector3f);
  var pitch, yaw, ax, az: float;
  begin
    //vector is in opengl coordinates (x right, y up, z pokes at your eye)
    //bitmap coordinates are 0,0 = top left corner
    if NonClosedHorizontally
      then yaw:= (-0.5 + (x / image.width)) * 2 * Pi
      else yaw:= (-0.5 + (x / (image.width - 1))) * 2 * Pi;
    pitch:= (0.5 - (y / (image.height - 1))) * Pi;
    vector[0]:= cos(pitch) * sin(yaw);
    vector[1]:= sin(pitch);
    vector[2]:= cos(pitch) * - cos(yaw);
  end;

  procedure TOutCubeMapLayer.BitmapToProjectVector(const x, y: float; out vector: TVector3f);
  //correct rotation to one of the 6 sides is provided by the matrix
  begin
    FillChar(vector, sizeof(vector), 0);
    vector[0]:= sin((Pi / 2) * (0.5 - (x / (image.width - 1))));
    vector[1]:= sin((Pi / 2) * (0.5 - (y / (image.height - 1))));
    vector[2]:= - 1 / sqrt(2);
    Normalize(vector);
    vector*= matrix;
  end;  

Oh, and have this nice sphere-mapped md3: http://chebmaster.com/_share/fullsphere.md3
Be thankful, I made it in Blender. In Blender, man!  Shocked Permanently losing some SANity points in the process  topsy because Blender interface was made by Chthulhu in Hell.

Quote
CheSkymp is a skybox composer optimized for fast reassembly with one click/launch. Meaning it operates in the same way as compilers do: There are sources, in this case images and the sky definition .INI file that are processed into output skyboxes.

The main advantage of this is the ability to keep several output skyboxes synchronized without doing that manually (e.g. you have one hard-alpha sky map and one blurred shadow sphere map).

A secondary advantage is that your sources are not changed during the composition process so there are no accumulating errors.

The main disadvantage is non-visual, you have to keep your skybox layout in your mind.

To work, CheSkymp needs a sky definition (using .INI file format) that describes the skybox(es) to make

NOTE#1: the [section]:ident notation I use in this readme refers to a string
ident=<value>
inside a block delineated by an opening string
[section]
That is all there is about the .INI file "syntax".

NOTE#2: all color calculations are performed in floating point and only culled to 0.0..1.0 when written to the output image. So layer blending modes like myltiply *can* surprise you, you *can* specify out-of-range colors like 2.0,2.2,11 and you *can* get and use *negative* brightness values.

NOTE#3: the decimal separator in floating-point values is dot, regardless of system locale and stuff.

NOTE#4: absolutely all operations on the input bitmaps use Lanczos filtering.

NOTE#5: the difinition is NOT case-sensitive. Unless you work with file names in Linux, there is absolutely no distinction between mylayer and MyLaYeR




[project]:input_path
   Input path where CheSkymp searches for source images. If not specified, the path to the .INI file will be used. Can be declared as relative.
   
[project]:output_path
   Output path where CheSkymp places the generated skybox bitmaps. If not specified, the .INI file path will be used.  Can be declared as relative.
   

[project]:debug   
   If set to 1, will screen any exceptions during bitmap generation, filling these pixels with magenta.
   
[project]:output_image_format
   Default is tga. Please note this is file extension without the dot that is passed directly to Vampyre Imaging Library. No checks are performed. It is up to you to use right kinds of formats and not try saving image with transparency into a JPEG.
   
[project]:supersampling
   NOT IMPLEMENTED YET Sad
   
   
[project]:output
   A list of comma-separated output layer identifiers. Please note that output image name is (usually) output layer identifier with extension added.
   Note that outputs can share input layers!
   
[<output layer>]:type
   The type of skybox being generated. Valid values are:
   sphere_map
      Bog standard sphere map. Forward vector (zero direction) is the exact middle of the bitmap.
   gzdoom_sky
      A specific kind of sphere map, distorted non-linearly to counteract non-linear stretching in GZDoom). Zenith and nadir regions are not present (so there is information loss when exporting tinto this format). The game engine fills zenith and nadir with solid color derived from the image edges averaged.
      The bitmap size is forced 1024x512
   q3_cube_map
      A cube map in the format used by Quake 3 and games built on its engine, like Open Arena. Six images will be generated, with suffixes _ft, _lf, _rt, _bk, _up and _dn.

[<output layer>]:layers
   A comma-separated list of layer identifiers
   Nothing is stopping you from including the same layer several times.
      
[<output layer>]:input_path
   Optional. Overrides the project-wide setting.

[<output layer>]:output_path
   Optional. Overrides the project-wide setting.

[<output layer>]:output_image_format
   Optional. Overrides the project-wide setting.

[<output layer>]:supersampling
   Optional. Overr-- NOT IMPLEMENTED YET, DAMMIT Sad
   
[<output layer>]:alpha
   Boolean value (set to 1 to activate). Determines if the image would have alpha channel. If not, black background would be in stead of transparent areas. Of course, output image format has to support it.
   
[<output layer>]:hdr
   !UNTESTED!
   Boolean value (set to 1 to activate). Output image would be unculled floating-point RGBA32F. Of course output image format has to support this.
   
[<output layer>]:flip
   Boolean value (set to 1 to activate). Causes the output image to be flipped horizontally.
   NOT supported by cube maps.


[<output sphere map layer>]:height   
[<output sphere map layer>]:sphere_width
   You can specify only one of them, then another one would be derived from assumption that width is double the height.
   Width is clipped to 8192, height to 4096
   
[<output sphere map layer>]:non_closed_horizontally   
   Boolean value (set to 1 to activate). Depending on the method you use to render your sphere map, it would be proper (with interpolation between the right and left side texels where the edges meet). By default it is assumed that you use a crude hack of a spherical model with its edges welded shut, so the right and the left map sides are exactly the same position and their pixels must be equal. basically, you sacrifice one pixel of your equator length for the sake of simplicity.
   
[<output cube map layer>]:height
   Width is always equal to height.
   
[<input layer>]:type
   The type of layer. Note that most layers support variety of blending modes a la Photoshop!
   group
      Layer group. Has its own blending mode as well as the list of child layers. see below.
   sphere_map
      Sphere map.
   sprite
      A single image projected onto the skybox.
   color
      Solid color. See below.
   gradient
      A conical gradient. See below.
   
   
[<input layer>]:bitmap
   Specifies image file for sphere map and sprite type layers, ignored otherwise.

[<input layer>]:flip
   Boolean value (set to 1 to activate). Causes the input image to be flipped horizontally.
   
[<input layer>]:opacity
   Floating-point value that defines the layer opacity. Default is 1.0
   
[<input layer>]:mode
   Blending mode. Default is Normal. Valid values are:
   Normal
   Multiply
   Divide
    Screen
   Overlay
   Dodge
   Burn
   Hard_Light
   Soft_Light
   Grain_Extract
   Grain_Merge
    Difference
   Addition
   Substract
   Darken_Only
   Lighten_Only,
    Hue
   Saturation
   Color
   Value
   For what they do, refer to any Photoshop / TheGIMP tutorial.
   Note that hue, saturation and color work differently from classic Photoshop behavior. When encountering source pixels with absolutely no color, white "color" will be produced instead of skipping that pixel and making the layer transparent.

[<input layer>]:yaw   
[<input layer>]:pitch   
[<input layer>]:roll
   Floating point numbers specifying layer rotations in degrees.
   Initially, any input layer is positioned around the forward vector (dead center of the sphere map bitmap).
   First, yaw (rotation around the vertical axis) is applied. Positive angle is right.
   Second, pitch (rotation around horizontal axis perpendiculat the direction vector) is applied. Positive angle is up (90=zenith, -90=nadir)
   Finally, roll (rotation around the direction vector) is applied. Positive angle is clockwise.
   Have no effect on solid color layers.
   
[<input layer>]:y_shift   
   Alternate way to specify pitch for sphere map and sprite type layers (ignored otherwise).
   The angle is in input bitmap pixels.
   Positive shift is UP.

[<input layer>]:x_shift   
   Alternate way to specify yaw for sphere map and sprite type layers (ignored otherwise).
   The angle is in input bitmap pixels.
   Positive shift is right.
   
   
NOTE: the sprite's direction vector goes through the center of its bitmap, so sprite with zero yaw and pitch will land in the center of the output sphere map.
   
[<sprite layer>]:angle
   Floating-point value in degrees dictating the size of the sprite on the sky. The angle is taken across the mid-point, across width or height (whichever is greater). Sprite edges cannot touch together, so maximum attainable angle for a square image is about 254 degrees (may be significantly larger for thin strips, but always less than 360).
   
[<sprite layer>]:size   
   Alternative way to set angle.
   This value is in pixels of the output bitmap. The resulting angle is calculated relative to the sphere map width (a good enough approximation), or double the cube map height (which is rough and imprecise).
   
[<sprite layer>]:force_aspect
   Floating-point value. Forces aspect (height to width ratio) regardless of the bitmap's actual dimensions.
   
[<sprite layer>]:aspect_correction   
   Floating-point value. Aspect (height to width ratio) is multiplied by this.
   Ignored if force_aspect is set.
   
[<solid color layer>]:color
   Three or four floating-point values separated by commas, for red, green, blue and optional alpha.
   If alpha is omitted, it is set to 1.0
   
[<gradient layer>]:start_color
   Three or four floating-point values separated by commas, for red, green, blue and optional alpha.
   If alpha is omitted, it is set to 1.0
   
[<gradient layer>]:end_color
   OPTIONAL. If omitted, the end color is the same as the start color but with alpha set to 0.
   Three or four floating-point values separated by commas, for red, green, blue and optional alpha.
   If alpha is omitted, it is set to 1.0
   
[<gradient layer>]:start_angle
[<gradient layer>]:end_angle
   Floating-point values culled to 0.0..180.0
   Please note that *all* gradients are conical, due to spherical nature of the sky space.
   If any omitted, 0 is assumed but you *have* to declare either.
   Angles less than start angle are filled with start color, and angles greater than the end angle are filled with end color.
   Examples:
      A fuzzy spot: end_angle=30, no start_angle
      A clear spot in a sphere of solid color: start_angle=30, no end_angle
      A basic sky with a blurred horizon: start_angle=80 end_angle=100, pitch=90, start color blue and end color brown.
   
[<gradient layer>]:power
   Floating-point value that dictates how non-linear the gradient would be.
   Culled to 0.0001..10000.0
   Default is 1.0
   Basically, a power of transitional value between 0.0 (start) and 1.0 (end) would be taken. Remember how power functions behave in this range: 0.5 is square root, so mid-point would be shifted towards the start. 2 is square, so mid-point would be shifted towards the end.
   
[<group layer>]:layers
   A comma-separated list of layer identifiers
   Please note that groups can share layers without limit! Make sure you don't create circular references or CheSkymp would hang.
« Last Edit: May 13, 2018, 04:23:37 am by cheb » Logged

Imma lazy dreamer. I achieved nothing.
Pages: [1]
  Print  
 
Jump to: