Library: five more screensaver backgrounds, from ARMSX3

ARMSX2 had Flurry alone. This brings over the Really Slick Screensavers tree
ARMSX3 already runs -- Flux, Plasma, SolarWinds, Hyperspace, Lattice and
Skyrocket -- as one libsavers.so beside libflurry.so.

Terry Welsh's savers, GPL-2.0-OR-LATER, which this tree may take under the "or
later" clause. Nothing here comes from rss-glx: that Linux port is GPL-2.0-only
and cannot go into ARMSX2 at all, which is why Lattice and Skyrocket were
hand-ported from the Windows sources upstream rather than taken from the neutral
versions rss-glx already has. The per-file header is what decides this, not the
repository's LICENSE file; they disagree.

The saver sources build with RS_XSCREENSAVER, selecting their platform-neutral
path, against the GLES2 shim in savers/compat and savers/gl1.c rather than being
rewritten -- so they stay re-pullable and recognisably his code.

Four things the tree carries that are not obvious from the sources:

  - The GL thread takes a 16MB stack. Skyrocket's World constructor declares a
    1024x1024x3 starmap as a local, and a default stack is a SIGSEGV inside
    memset before the first frame draws.
  - Each saver is compiled through a *_unit.cpp that wraps it in a namespace.
    They all declare draw/idleProc/cleanUp/readyToDraw, because each was built as
    its own executable, and two in one .so collide at link. The shared libraries
    must be pre-included OUTSIDE the namespace or the declarations never match
    their definitions -- which compiles clean and fails at link.
  - The lifecycle is serialised by a mutex and a generation token: gl1 keeps its
    state in one global, so a view may only free the run it started. Without it
    the first selection works and every later one is black.
  - gl1_frame_begin() masks alpha off after the first frames. These savers fade
    the previous frame rather than clearing, and that fade writes destination
    alpha, dragging a composited surface transparent -- which reaches the screen
    as full-screen TV static.

Hyperspace runs with dShaders = 0 (its ARB shader path needs objects GLES2 does
not have; upstream exposes this as -shaders 0 and falls back to it itself), and
Skyrocket with dSound = 0 (upstream drives OpenAL and bakes ~7MB of samples into
headers; soundEngine.h is a stub here). Hyperspace and Skyrocket ship no presets
upstream, so neither shows a preset picker -- invented ranges are what made
earlier ports fail to start.

SaverGlView replaces FlurryGlView, which it supersedes: it hosts Flurry and the
Really Slick savers behind one SaverSpec, and keeping both would have declared
FlurryNative twice in one package. Helios is deliberately absent -- it rendered
but animated wrongly and the cause was never found.
This commit is contained in:
jpolo1224
2026-08-21 15:26:28 -04:00
parent c3f859270f
commit 81d0a3e510
129 changed files with 107261 additions and 35 deletions
@@ -102,6 +102,103 @@ target_compile_options(flurry PRIVATE -O2 -ffast-math -Wall -Wno-unused-paramete
target_link_libraries(flurry log GLESv2 m)
# ---------------------------------------------------------------------------
# Really Slick Screensavers -> libsavers.so
#
# Terry Welsh's savers (GPL-2.0-or-later), compiled against a GLES2 shim rather than rewritten.
# The saver sources are BYTE-IDENTICAL to upstream: they are built with RS_XSCREENSAVER, which
# selects their platform-neutral path, and everything that path expects is answered by
# compat/. That keeps them re-pullable and keeps them recognisably his code.
#
# Separate from the flurry target because these are C++ and Flurry is C99, and because Flurry's
# shim is a different, smaller one. If a saver ever needs Flurry's, the two can merge.
add_library(savers SHARED
savers/savers_jni.cpp
savers/savers_platform.cpp
savers/gl1.c
# Each saver is compiled through a *_unit.cpp that wraps it in a namespace: they all
# declare the same globals (draw, idleProc, cleanUp, readyToDraw) because each was built
# as its own executable, and two in one .so collide at link time.
savers/flux/flux_unit.cpp
savers/flux/flux_port.cpp
savers/plasma/plasma_unit.cpp
savers/plasma/plasma_port.cpp
savers/solarwinds/solarwinds_unit.cpp
savers/solarwinds/solarwinds_port.cpp
# Hyperspace. extensions.cpp is deliberately NOT built: it is the WIN32/GLX loader for the
# ARB shader path, and the port takes the non-shader path instead. The entry points it
# would have resolved are declared in compat/arb_shaders.h and defined in savers_platform.
savers/hyperspace/unit_causticTextures.cpp
savers/hyperspace/unit_flare.cpp
savers/hyperspace/unit_goo.cpp
savers/hyperspace/unit_hyperspace.cpp
savers/hyperspace/unit_splinePath.cpp
savers/hyperspace/unit_starBurst.cpp
savers/hyperspace/unit_stretchedParticle.cpp
savers/hyperspace/unit_tunnel.cpp
savers/hyperspace/unit_wavyNormalCubeMaps.cpp
savers/hyperspace/hyperspace_port.cpp
savers/lattice/lattice_unit.cpp
savers/lattice/lattice_port.cpp
# Skyrocket. soundEngine.cpp is NOT built and its ~7MB of samples are not shipped: upstream
# drives OpenAL, the port runs with dSound = 0, and soundEngine.h is a stub.
savers/skyrocket/unit_flare.cpp
savers/skyrocket/unit_particle.cpp
savers/skyrocket/unit_shockwave.cpp
savers/skyrocket/unit_skyrocket.cpp
savers/skyrocket/unit_smoke.cpp
savers/skyrocket/unit_world.cpp
savers/skyrocket/skyrocket_port.cpp
savers/rslibs/rsMath/rsVec.cpp
savers/rslibs/rsMath/rsVec4.cpp
savers/rslibs/rsMath/rsMatrix.cpp
savers/rslibs/rsMath/rsQuat.cpp
savers/rslibs/Rgbhsl/Rgbhsl.cpp
# Implicit surfaces: Hyperspace's "goo" is a marching-cubes isosurface.
savers/rslibs/Implicit/impSurface.cpp
savers/rslibs/Implicit/impCubeVolume.cpp
savers/rslibs/Implicit/impCubeTables.cpp
savers/rslibs/Implicit/impShape.cpp
savers/rslibs/Implicit/impSphere.cpp
savers/rslibs/Implicit/impCapsule.cpp
savers/rslibs/Implicit/impEllipsoid.cpp
savers/rslibs/Implicit/impHexahedron.cpp
savers/rslibs/Implicit/impRoundedHexahedron.cpp
savers/rslibs/Implicit/impKnot.cpp
savers/rslibs/Implicit/impTorus.cpp
)
target_include_directories(savers PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}/savers
${CMAKE_CURRENT_SOURCE_DIR}/savers/compat
${CMAKE_CURRENT_SOURCE_DIR}/savers/rslibs
${CMAKE_CURRENT_SOURCE_DIR}/savers/flux
${CMAKE_CURRENT_SOURCE_DIR}/savers/plasma
${CMAKE_CURRENT_SOURCE_DIR}/savers/solarwinds
${CMAKE_CURRENT_SOURCE_DIR}/savers/hyperspace
${CMAKE_CURRENT_SOURCE_DIR}/savers/lattice
${CMAKE_CURRENT_SOURCE_DIR}/savers/skyrocket
)
# RS_XSCREENSAVER picks the savers' platform-neutral path. Without it they compile their Win32
# shell and nothing links.
target_compile_definitions(savers PRIVATE RS_XSCREENSAVER)
set_target_properties(savers PROPERTIES C_STANDARD 99 CXX_STANDARD 17)
# -ffast-math for the same reason as Flurry: particle simulation judged entirely by eye.
target_compile_options(savers PRIVATE -O2 -ffast-math -Wall -Wno-unused-parameter)
target_link_libraries(savers log GLESv2 m)
add_subdirectory(3rdparty/adrenotools)
# librashader: RetroArch (.slangp) shader chains for the GS post-process path, for BOTH
@@ -213,6 +310,7 @@ target_link_libraries(${ARMSX2_EMUCORE_LIBRARY_NAME} PRIVATE
# this build would make it. A missing .so stays invisible until someone switches the background
# on and the view throws UnsatisfiedLinkError.
add_dependencies(${ARMSX2_EMUCORE_LIBRARY_NAME} flurry)
add_dependencies(${ARMSX2_EMUCORE_LIBRARY_NAME} savers)
target_include_directories(${ARMSX2_EMUCORE_LIBRARY_NAME} PRIVATE
"${ARMSX2_ROOT}"
@@ -0,0 +1,5 @@
/* Redirects the savers' <GL/gl.h> to the GLES2 shim. See ../../gl1.h. */
#ifndef SAVERS_COMPAT_GL_H
#define SAVERS_COMPAT_GL_H
#include "gl1.h"
#endif
@@ -0,0 +1,6 @@
/* The Implicit library includes <GL/glext.h> for the multitexture entry points. GLES2 has
* those in its core header, which gl1.h already pulls in. */
#ifndef SAVERS_COMPAT_GLEXT_H
#define SAVERS_COMPAT_GLEXT_H
#include "gl1.h"
#endif
@@ -0,0 +1,6 @@
/* Redirects the savers' <GL/glu.h> to the GLES2 shim, which answers gluPerspective,
* gluLookAt and the quadric spheres. See ../../gl1.h. */
#ifndef SAVERS_COMPAT_GLU_H
#define SAVERS_COMPAT_GLU_H
#include "gl1.h"
#endif
@@ -0,0 +1,73 @@
/*
* The ARB_shader_objects surface Hyperspace expects.
*
* Hyperspace has two rendering paths and picks between them with dShaders. The shader path uses
* ARB assembly-era shader objects, which GLES2 does not have at all. Rather than translate them,
* the types are declared so the code compiles and the entry points are looked up at runtime --
* on GLES2 they come back null, extensions.cpp reports the extension missing, and the saver
* takes its own non-shader path, which is a supported upstream configuration rather than
* something invented here.
*/
#ifndef SAVERS_COMPAT_ARB_SHADERS_H
#define SAVERS_COMPAT_ARB_SHADERS_H
#include <EGL/egl.h>
#include "gl1.h"
typedef unsigned int GLhandleARB;
typedef char GLcharARB;
typedef void (*PFNGLACTIVETEXTUREARBPROC)(GLenum);
typedef void (*PFNGLMULTITEXCOORD2FARBPROC)(GLenum, GLfloat, GLfloat);
typedef GLhandleARB (*PFNGLCREATEPROGRAMOBJECTARBPROC)(void);
typedef GLhandleARB (*PFNGLCREATESHADEROBJECTARBPROC)(GLenum);
typedef void (*PFNGLSHADERSOURCEARBPROC)(GLhandleARB, GLsizei, const GLcharARB **, const GLint *);
typedef void (*PFNGLCOMPILESHADERARBPROC)(GLhandleARB);
typedef void (*PFNGLATTACHOBJECTARBPROC)(GLhandleARB, GLhandleARB);
typedef void (*PFNGLLINKPROGRAMARBPROC)(GLhandleARB);
typedef void (*PFNGLUSEPROGRAMOBJECTARBPROC)(GLhandleARB);
typedef GLint (*PFNGLGETUNIFORMLOCATIONARBPROC)(GLhandleARB, const GLcharARB *);
typedef void (*PFNGLUNIFORM1IARBPROC)(GLint, GLint);
typedef void (*PFNGLUNIFORM1FARBPROC)(GLint, GLfloat);
typedef void (*PFNGLUNIFORM4FARBPROC)(GLint, GLfloat, GLfloat, GLfloat, GLfloat);
typedef void (*PFNGLGETOBJECTPARAMETERIVARBPROC)(GLhandleARB, GLenum, GLint *);
typedef void (*PFNGLGETINFOLOGARBPROC)(GLhandleARB, GLsizei, GLsizei *, GLcharARB *);
typedef void (*PFNGLDELETEOBJECTARBPROC)(GLhandleARB);
/* Multitexture unit names; core in GLES2 under the unsuffixed spelling. */
#ifndef GL_TEXTURE0_ARB
#define GL_TEXTURE0_ARB GL_TEXTURE0
#define GL_TEXTURE1_ARB GL_TEXTURE1
#define GL_TEXTURE2_ARB GL_TEXTURE2
#define GL_TEXTURE3_ARB GL_TEXTURE3
#endif
#ifndef GL_VERTEX_SHADER_ARB
#define GL_VERTEX_SHADER_ARB 0x8B31
#define GL_FRAGMENT_SHADER_ARB 0x8B30
#define GL_OBJECT_COMPILE_STATUS_ARB 0x8B81
#define GL_OBJECT_LINK_STATUS_ARB 0x8B82
#endif
/* Upstream only includes its own extensions.h under WIN32 -- its X11 build relies on the system
* GL exporting these symbols directly, which Mesa does and Android does not. So they are
* declared here, at global scope, where the savers' namespaced code still finds them.
*
* Nothing calls them: every use sits behind dShaders, which the port forces off. glActiveTexture
* is the one exception, being core in GLES2, so that one points at the real function. */
extern PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
extern PFNGLUNIFORM1IARBPROC glUniform1iARB;
/* Upstream resolves entry points through the platform's GetProcAddress; on Android that is
* EGL's. Anything ARB-only returns null here, which is the point. */
#define glXGetProcAddressARB(name) ((void *) eglGetProcAddress((const char *) (name)))
#endif
@@ -0,0 +1,11 @@
/* Case alias: Helios includes <rgbhsl/rgbhsl.h>, Flux includes <Rgbhsl/Rgbhsl.h>. Windows did
* not care and neither does macOS, but a case-sensitive build host would fail on one of them.
*
* The target is spelled as an explicit relative path, NOT as <Rgbhsl/Rgbhsl.h>. On a
* case-insensitive filesystem the search path would match "compat/Rgbhsl" to this very
* directory and the file would include itself -- the guard then silently swallows it and the
* real declarations never arrive. */
#ifndef SAVERS_RGBHSL_LOWER_ALIAS_H
#define SAVERS_RGBHSL_LOWER_ALIAS_H
#include "../../rslibs/Rgbhsl/Rgbhsl.h"
#endif
@@ -0,0 +1,57 @@
/*
* Stands in for the X11 screensaver shell (upstream rsXScreenSaver, LGPL-2.1).
*
* The savers carry three platform paths: WIN32, RS_XSCREENSAVER, and nothing. The X11 one is
* closest to what a background needs -- it exposes initSaver(), reshape(), draw(), idleProc()
* and cleanUp() as plain functions and leaves the whole Win32 dialog shell out -- so we compile
* with RS_XSCREENSAVER defined and answer what that path expects here.
*
* The point of doing it this way is that the saver sources stay BYTE-IDENTICAL to upstream.
* Nothing is patched, so they can be re-pulled, and they stay recognisably Terry Welsh's code,
* which their GPL headers ask us to keep intact.
*
* Declarations mirror upstream's types exactly -- note these flags are int, not bool.
*/
#ifndef SAVERS_COMPAT_RSXSCREENSAVER_H
#define SAVERS_COMPAT_RSXSCREENSAVER_H
#include <string>
#include <vector>
#include "rsUtility/rsTimer.h"
/* The savers poll these before drawing. The host view stops calling us when it is not visible
* rather than flagging suspension, so they stay 0. */
extern int checkingPassword;
extern int isSuspended;
extern int doingPreview;
/* Pixel-format bits the X11 host reported; meaningless here and read only by savers deciding
* whether they can rely on buffer preservation. */
extern int pfd_swap_exchange;
extern int pfd_swap_copy;
extern unsigned int dFrameRateLimit;
/* The on-screen frame-rate overlay. Left off -- it is debug furniture for a wallpaper. */
extern int kStatistics;
/* GLX buffer swapping is the host view's job: it owns the EGL surface. */
extern void *xdisplay;
extern unsigned long xwindow;
#define glXSwapBuffers(dpy, win) ((void) 0)
/* The savers build their frame-rate string with a bare to_string(); upstream picks it up from
* this header's include chain. */
using std::to_string;
/* Command-line parsing has no meaning here -- settings arrive through each saver's port API,
* which calls setDefaults() and then assigns the globals directly. Accepting and ignoring the
* call keeps handleCommandLine() compiling unmodified. */
inline int getArgumentsValue(int, char **, std::string, std::string &) { return 0; }
template <typename T>
inline int getArgumentsValue(int, char **, std::string, T &) { return 0; }
template <typename T>
inline int getArgumentsValue(int, char **, std::string, T &, T, T) { return 0; }
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,68 @@
/*
* Android entry points for Flux.
*
* flux.cpp is upstream, byte-identical. flux_unit.cpp compiles it inside a namespace, with
* RS_XSCREENSAVER defined so it takes its platform-neutral path -- initSaver(), reshape(),
* idleProc(), cleanUp() as plain functions, and no Win32 shell. Everything that path expects
* from an X11 host is answered by compat/rsXScreenSaver/rsXScreenSaver.h.
*/
#include "gl1.h"
namespace saver_flux {
void setDefaults(int which);
void initSaver();
void reshape(int width, int height);
void idleProc();
void cleanUp();
extern int readyToDraw;
extern int dGeometry;
}
namespace { bool g_started = false; }
extern "C" {
/* preset is 1..6, matching the saver's own DEFAULTS1..DEFAULTS6. */
int flux_port_new(int preset)
{
if (g_started) return 1;
if (!gl1_init()) return 0;
if (preset < 1 || preset > 6) preset = 1;
saver_flux::setDefaults(preset);
/* Sphere geometry needs fixed-function lighting, which the shim does not emulate -- the
* spheres would draw flat and unlit, which looks worse than the alternative rather than
* merely different. Points and the textured "lights" need no lighting, so fall back to the
* lights, which is the mode the effect is known for anyway. */
if (saver_flux::dGeometry == 1) saver_flux::dGeometry = 2;
saver_flux::initSaver();
g_started = saver_flux::readyToDraw != 0;
return g_started ? 1 : 0;
}
void flux_port_resize(int width, int height)
{
if (width > 0 && height > 0) saver_flux::reshape(width, height);
}
/* idleProc() does the frame timing itself and calls draw(). */
void flux_port_draw()
{
if (!g_started) return;
gl1_frame_begin();
saver_flux::idleProc();
}
void flux_port_free()
{
if (!g_started) return;
saver_flux::cleanUp();
gl1_shutdown();
saver_flux::readyToDraw = 0;
g_started = false;
}
} /* extern "C" */
@@ -0,0 +1,25 @@
/*
* Compiles flux.cpp inside a namespace.
*
* Every Really Slick saver declares the same global names -- draw(), idleProc(), cleanUp(),
* setDefaults(), readyToDraw, aspectRatio, dSize, dBlur -- because each was built as its own
* executable. Two of them in one .so collide at link time.
*
* The headers are pulled in FIRST, at global scope, so that the copies inside the namespace hit
* their include guards and expand to nothing. flux.cpp itself is then included unchanged, which
* is the point: it stays byte-identical to upstream and can be re-pulled without re-patching.
*/
#include <rsXScreenSaver/rsXScreenSaver.h>
#include <stdio.h>
#include <math.h>
#include <string>
#include <rsText/rsText.h>
#include <rsMath/rsMath.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <Rgbhsl/Rgbhsl.h>
namespace saver_flux {
#include "flux.cpp"
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,355 @@
/*
* Enough OpenGL 1.x for the Really Slick Screensavers, implemented on GLES2.
*
* Terry Welsh's screensavers are late-90s/2000s OpenGL: immediate mode (glBegin/glVertex3f),
* the fixed-function matrix stack (glPushMatrix/glTranslatef/gluPerspective) and fixed-function
* lighting. GLES2 has none of it. Rather than rewrite each renderer -- and lose the property
* that these stay recognisably his code, which the GPL headers on them ask us to preserve --
* the calls are redirected here and answered with one shader and a matrix stack.
*
* This is deliberately a SEPARATE shim from flurry/gl_compat.h. That one is Flurry-shaped:
* client-side vertex arrays and a 2D ortho, with glMatrixMode a no-op. Making its matrices real
* would risk a renderer that took a long time to get right. Once a couple of savers are running
* on this file, Flurry can move over to it and the two can merge.
*
* NOT emulated yet, because no ported saver has needed it: fixed-function lighting. glLightfv
* and friends are accepted and ignored, so anything relying on them draws unlit. Flux calls
* them -- check what it actually looks like before deciding whether to implement per-vertex
* lighting here or bake it into that saver.
*/
#ifndef SAVERS_GL1_H
#define SAVERS_GL1_H
#include <GLES2/gl2.h>
#ifdef __cplusplus
extern "C" {
#endif
/* GL 1.x names GLES2 does not define. */
#ifndef GL_QUADS
#define GL_QUADS 0x0007
#endif
#ifndef GL_QUAD_STRIP
#define GL_QUAD_STRIP 0x0008
#endif
#ifndef GL_POLYGON
#define GL_POLYGON 0x0009
#endif
#ifndef GL_MODELVIEW
#define GL_MODELVIEW 0x1700
#endif
#ifndef GL_PROJECTION
#define GL_PROJECTION 0x1701
#endif
#ifndef GL_ALPHA_TEST
#define GL_ALPHA_TEST 0x0BC0
#endif
#ifndef GL_LIGHTING
#define GL_LIGHTING 0x0B50
#endif
#ifndef GL_LIGHT0
#define GL_LIGHT0 0x4000
#endif
#ifndef GL_NORMALIZE
#define GL_NORMALIZE 0x0BA1
#endif
#ifndef GL_COLOR_MATERIAL
#define GL_COLOR_MATERIAL 0x0B57
#endif
#ifndef GL_POINT_SMOOTH
#define GL_POINT_SMOOTH 0x0B10
#endif
#ifndef GL_LINE_SMOOTH
#define GL_LINE_SMOOTH 0x0B20
#endif
#ifndef GL_COMPILE
#define GL_COMPILE 0x1300
#endif
#ifndef GL_COMPILE_AND_EXECUTE
#define GL_COMPILE_AND_EXECUTE 0x1301
#endif
#ifndef GL_TEXTURE_GEN_S
#define GL_TEXTURE_GEN_S 0x0C60
#endif
#ifndef GL_TEXTURE_GEN_T
#define GL_TEXTURE_GEN_T 0x0C61
#endif
/* Cube maps are core in GLES2; only the ARB spelling is missing. */
#ifndef GL_TEXTURE_CUBE_MAP_ARB
#define GL_TEXTURE_CUBE_MAP_ARB GL_TEXTURE_CUBE_MAP
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB GL_TEXTURE_CUBE_MAP_POSITIVE_X
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB GL_TEXTURE_CUBE_MAP_NEGATIVE_X
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB GL_TEXTURE_CUBE_MAP_POSITIVE_Y
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB GL_TEXTURE_CUBE_MAP_POSITIVE_Z
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB GL_TEXTURE_CUBE_MAP_NEGATIVE_Z
#define GL_REFLECTION_MAP_ARB 0x8512
#define GL_NORMAL_MAP_ARB 0x8511
#endif
#ifndef GL_FOG
#define GL_FOG 0x0B60
#endif
#ifndef GL_MODELVIEW_MATRIX
#define GL_MODELVIEW_MATRIX 0x0BA6
#define GL_PROJECTION_MATRIX 0x0BA7
#endif
#ifndef GL_UNPACK_ROW_LENGTH
#define GL_UNPACK_ROW_LENGTH 0x0CF2
#endif
/* GLES2 has only CLAMP_TO_EDGE. The savers use GL_CLAMP on textures they never sample outside
* [0,1], so the border-vs-edge difference is not reachable. */
#ifndef GL_CLAMP
#define GL_CLAMP GL_CLAMP_TO_EDGE
#endif
#ifndef GL_DECAL
#define GL_DECAL 0x2101
#endif
#ifndef GL_VERTEX_ARRAY
#define GL_VERTEX_ARRAY 0x8074
#endif
#ifndef GL_NORMAL_ARRAY
#define GL_NORMAL_ARRAY 0x8075
#endif
#ifndef GL_COLOR_ARRAY
#define GL_COLOR_ARRAY 0x8076
#endif
#ifndef GL_TEXTURE_COORD_ARRAY
#define GL_TEXTURE_COORD_ARRAY 0x8078
#endif
#ifndef GL_N3F_V3F
#define GL_N3F_V3F 0x2A25
#endif
#ifndef GL_S
#define GL_S 0x2000
#endif
#ifndef GL_T
#define GL_T 0x2001
#endif
#ifndef GL_TEXTURE_GEN_MODE
#define GL_TEXTURE_GEN_MODE 0x2500
#endif
#ifndef GL_SPHERE_MAP
#define GL_SPHERE_MAP 0x2402
#endif
#ifndef GL_TEXTURE_2D_ENABLE_COMPAT
#define GL_TEXTURE_2D_ENABLE_COMPAT 0x0DE1
#endif
/* Lifetime. gl1_init needs a current context; gl1_lost forgets GL names without touching
* them, for when the context has already gone away underneath us. */
int gl1_init(void);
void gl1_shutdown(void);
void gl1_lost(void);
/* Matrix stack. */
void gl1_matrix_mode(GLenum mode);
void gl1_load_identity(void);
void gl1_push_matrix(void);
void gl1_pop_matrix(void);
void gl1_load_matrixf(const float *m);
void gl1_mult_matrixf(const float *m);
void gl1_translatef(float x, float y, float z);
void gl1_scalef(float x, float y, float z);
void gl1_rotatef(float angle, float x, float y, float z);
void gl1_ortho(double l, double r, double b, double t, double n, double f);
void gl1_frustum(double l, double r, double b, double t, double n, double f);
void gl1_perspective(double fovy, double aspect, double zn, double zf);
void gl1_look_at(double ex, double ey, double ez, double cx, double cy, double cz,
double ux, double uy, double uz);
/* Immediate mode. */
void gl1_begin(GLenum mode);
void gl1_end(void);
void gl1_vertex2f(float x, float y);
void gl1_vertex3f(float x, float y, float z);
void gl1_vertex3fv(const float *v);
void gl1_texcoord2f(float s, float t);
void gl1_color3f(float r, float g, float b);
void gl1_color4f(float r, float g, float b, float a);
void gl1_color3fv(const float *c);
void gl1_color4fv(const float *c);
void gl1_normal3f(float x, float y, float z);
void gl1_normal3fv(const float *n);
void gl1_texcoord2fv(const float *t);
/* The savers read the matrices back to hand them to gluProject. GLES2 has no matrix state at
* all, so these answer from the shim's own stack. GLdouble is likewise absent. */
typedef double gl1_double;
void gl1_get_floatv(GLenum pname, float *params);
void gl1_get_doublev(GLenum pname, gl1_double *params);
/* Object space to window space, for savers that place 2D overlays over 3D positions. */
int gl1_project(double ox, double oy, double oz, double *winx, double *winy, double *winz);
/* Display lists.
*
* The savers build a shape once (a sphere, or a textured quad) and replay it per particle with
* a different matrix each time -- so this is not an optimisation to skip, it IS how they draw.
* Recording captures the immediate-mode batches; replaying re-draws them under whatever the
* matrices currently are. */
GLuint gl1_gen_lists(GLsizei range);
void gl1_new_list(GLuint list, GLenum mode);
void gl1_end_list(void);
void gl1_call_list(GLuint list);
void gl1_delete_lists(GLuint list, GLsizei range);
/* GLU quadrics. Only spheres are used, and only as list content, so the quadric object is a
* token and the sphere is emitted through the immediate-mode path above. */
typedef struct gl1_quadric gl1_quadric;
gl1_quadric *gl1_new_quadric(void);
void gl1_delete_quadric(gl1_quadric *q);
void gl1_sphere(gl1_quadric *q, double radius, int slices, int stacks);
/* Vertex arrays.
*
* The Implicit surface library draws through these, with real VBOs -- so unlike the immediate
* mode above, the data is already in a form GLES2 accepts and only the fixed-function
* plumbing (which attribute each pointer feeds, and the enable flags) has to be emulated. */
void gl1_vertex_pointer(GLint size, GLenum type, GLsizei stride, const void *ptr);
void gl1_normal_pointer(GLenum type, GLsizei stride, const void *ptr);
void gl1_color_pointer(GLint size, GLenum type, GLsizei stride, const void *ptr);
void gl1_texcoord_pointer(GLint size, GLenum type, GLsizei stride, const void *ptr);
void gl1_enable_client_state(GLenum cap);
void gl1_disable_client_state(GLenum cap);
void gl1_interleaved_arrays(GLenum format, GLsizei stride, const void *ptr);
void gl1_draw_arrays(GLenum mode, GLint first, GLsizei count);
void gl1_draw_elements(GLenum mode, GLsizei count, GLenum type, const void *indices);
void gl1_multi_draw_elements(GLenum mode, const GLsizei *count, GLenum type,
const void *const *indices, GLsizei primcount);
/* State GLES2 rejects outright; an invalid enum would leave a sticky error for everything
* after it, so these are filtered rather than passed through. */
void gl1_enable(GLenum cap);
void gl1_disable(GLenum cap);
void gl1_point_size(float size);
/* Draw counters since the last call, for working out whether a black saver is drawing nothing
* or drawing something invisible. Resets on read. */
void gl1_stats_take(int *batches, long *vertices, unsigned *last_error);
/* Call at the top of every frame, before the saver draws.
*
* Handles the two things that make an accumulating saver show TV static on a composited
* surface, both learned the hard way from Flurry:
* - the buffer starts UNDEFINED, so the first few frames are cleared outright; a saver that
* fades instead of clearing never establishes a known background on its own
* - the fade writes destination ALPHA as well as colour, dragging the surface toward
* transparent so the window compositor blends whatever is behind it. Alpha is set to 1 once
* and then masked off, which leaves RGB and the trails untouched. */
void gl1_frame_begin(void);
/* GL 1.x let internalformat be a component COUNT (1..4). GLES2 requires the actual enum and
* rejects the number with GL_INVALID_ENUM -- which leaves a black texture and a sticky error
* blamed on whatever runs next. Flux passes 1. */
/* GLU built the mip chain on the CPU; GLES2 has glGenerateMipmap. Same component-count
* translation as above applies to the internalformat argument. */
void gl1_build_2d_mipmaps(GLenum target, GLint components, GLsizei width, GLsizei height,
GLenum format, GLenum type, const void *data);
/* GLES2 accepts neither GL_FLOAT pixel data nor GL_UNPACK_ROW_LENGTH. Plasma uses both: it
* keeps a float RGB map and uploads a sub-rectangle of it. These repack into tight bytes. */
void gl1_pixel_store_i(GLenum pname, GLint param);
void gl1_tex_sub_image_2d(GLenum target, GLint level, GLint xoff, GLint yoff, GLsizei width,
GLsizei height, GLenum format, GLenum type, const void *pixels);
void gl1_tex_image_2d(GLenum target, GLint level, GLint internalformat, GLsizei width,
GLsizei height, GLint border, GLenum format, GLenum type,
const void *pixels);
#ifdef __cplusplus
}
#endif
/* The redirection. Included by each saver's sources ahead of any GL use.
*
* gl1.c MUST #undef every name below before defining the functions, or each implementation
* calls itself -- and at -O2 that becomes a silent infinite loop rather than a crash. */
#define glMatrixMode gl1_matrix_mode
#define glLoadIdentity gl1_load_identity
#define glPushMatrix gl1_push_matrix
#define glPopMatrix gl1_pop_matrix
#define glLoadMatrixf gl1_load_matrixf
#define glMultMatrixf gl1_mult_matrixf
#define glTranslatef gl1_translatef
#define glScalef gl1_scalef
#define glRotatef gl1_rotatef
#define glOrtho gl1_ortho
#define glFrustum gl1_frustum
#define gluPerspective gl1_perspective
#define gluLookAt gl1_look_at
#define glBegin gl1_begin
#define glEnd gl1_end
#define glVertex2f gl1_vertex2f
#define glVertex3f gl1_vertex3f
#define glVertex3fv gl1_vertex3fv
#define glTexCoord2f gl1_texcoord2f
#define glColor3f gl1_color3f
#define glColor4f gl1_color4f
#define glColor3fv gl1_color3fv
#define glColor4fv gl1_color4fv
#define glNormal3f gl1_normal3f
#define glNormal3fv gl1_normal3fv
#define glTexCoord2fv gl1_texcoord2fv
#define glEnable gl1_enable
#define glDisable gl1_disable
#define glPointSize gl1_point_size
#define glGenLists gl1_gen_lists
#define glNewList gl1_new_list
#define glEndList gl1_end_list
#define glCallList gl1_call_list
#define glDeleteLists gl1_delete_lists
#define GLUquadricObj gl1_quadric
#define gluNewQuadric gl1_new_quadric
#define gluDeleteQuadric gl1_delete_quadric
#define gluSphere gl1_sphere
#define glTexImage2D gl1_tex_image_2d
#define gluBuild2DMipmaps gl1_build_2d_mipmaps
#define glTexSubImage2D gl1_tex_sub_image_2d
#define glPixelStorei gl1_pixel_store_i
#define gluOrtho2D(l, r, b, t) gl1_ortho((l), (r), (b), (t), -1.0, 1.0)
/* The savers pass the matrices and viewport they just queried; this shim keeps its own copies,
* so those arguments are ignored and the live state is used instead. */
#define gluProject(ox, oy, oz, mv, pj, vp, wx, wy, wz) \
gl1_project((ox), (oy), (oz), (wx), (wy), (wz))
/* No analogue, and nothing ported so far depends on the effect. */
#define glShadeModel(m) ((void) 0)
#define glAlphaFunc(f, r) ((void) 0)
#define glTexEnvf(t, p, v) ((void) 0)
#define glTexEnvi(t, p, v) ((void) 0)
#define glLightfv(l, p, v) ((void) 0)
#define glLightf(l, p, v) ((void) 0)
#define glMaterialfv(f, p, v) ((void) 0)
#define glMaterialf(f, p, v) ((void) 0)
#define glColorMaterial(f, m) ((void) 0)
#define glFogfv(p, v) ((void) 0)
#define glFogf(p, v) ((void) 0)
#define glFogi(p, v) ((void) 0)
#define glHint(t, m) ((void) 0)
#define glDrawBuffer(m) ((void) 0)
#define glReadBuffer(m) ((void) 0)
#define glGetFloatv gl1_get_floatv
#define glGetDoublev gl1_get_doublev
#define GLdouble gl1_double
#define glFinish() ((void) 0)
#define glPolygonMode(f, m) ((void) 0)
/* The generation MODE is not stored: GL_SPHERE_MAP is the only one the savers ask for, and
* gl1_vertex3f implements exactly that. A saver wanting GL_OBJECT_LINEAR or GL_EYE_LINEAR
* would silently get sphere mapping instead, so check this if one ever looks wrong. */
#define glVertexPointer gl1_vertex_pointer
#define glNormalPointer gl1_normal_pointer
#define glColorPointer gl1_color_pointer
#define glTexCoordPointer gl1_texcoord_pointer
#define glEnableClientState gl1_enable_client_state
#define glDisableClientState gl1_disable_client_state
#define glInterleavedArrays gl1_interleaved_arrays
#define glDrawArrays gl1_draw_arrays
#define glDrawElements gl1_draw_elements
#define glMultiDrawElements gl1_multi_draw_elements
#define glTexGeni(coord, pname, param) ((void) 0)
#define glTexGenfv(coord, pname, param) ((void) 0)
#define glLightModeli(p, v) ((void) 0)
#define glLightModelfv(p, v) ((void) 0)
#endif /* SAVERS_GL1_H */
@@ -0,0 +1,461 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef WIN32
#include <windows.h>
extern HDC hdc;
#endif
#ifdef RS_XSCREENSAVER
#include <rsXScreenSaver/rsXScreenSaver.h>
#endif
#include <iostream>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <rsMath/rsMath.h>
#include "causticTextures.h"
causticTextures::causticTextures(int keys, int frames, int res, int size, float depth, float wa, float rm){
int i, j, k;
int xminus, xplus, zminus, zplus;
int viewport[4];
unsigned char* bitmap = new unsigned char[size * size * 3];
// initialize dimensions
numKeys = keys;
if(numKeys < 2)
numKeys = 2;
numFrames = frames;
if(numFrames < numKeys * 2)
numFrames = numKeys * 2;
geoRes = res;
if(geoRes < 8)
geoRes = 8;
texSize = size;
if(texSize < 8)
texSize = 8;
waveAmp = wa;
refractionMult = rm;
caustictex = new GLuint[numFrames];
glGenTextures(numFrames, caustictex);
// allocate memory
x = new float[geoRes + 1];
z = new float[geoRes + 1];
y = new float**[numFrames];
for(k=0; k<numFrames; k++){
y[k] = new float*[geoRes];
for(i=0; i<geoRes; i++)
y[k][i] = new float[geoRes];
}
xz = new float**[geoRes + 1];
for(i=0; i<=geoRes; i++){
xz[i] = new float*[geoRes + 1];
for(j=0; j<=geoRes; j++)
xz[i][j] = new float[2];
}
intensity = new float*[geoRes + 1];
for(i=0; i<=geoRes; i++)
intensity[i] = new float[geoRes + 1];
// set x and z geometry positions
for(i=0; i<=geoRes; i++){
x[i] = float(i) / float(geoRes);
z[i] = float(i) / float(geoRes);
}
// set y geometry positions (altitudes)
// fractal altitudes is sort of ugly, so I don't use it
//makeFractalAltitudes();
makeTrigAltitudes();
// prepare to draw textures
glGetIntegerv(GL_VIEWPORT, viewport);
glViewport(0, 0, texSize, texSize);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -0.5f, 0.5f);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glRotatef(-90.0f, 1, 0, 0);
glReadBuffer(GL_BACK);
//glPixelStorei(GL_UNPACK_ROW_LENGTH, texSize);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glDisable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glEnable(GL_BLEND);
// project vertices and create textures
float recvert = float(geoRes) * 0.5f; // reciprocal of vertical component of light ray
for(k=0; k<numFrames; k++){
// compute projected offsets
// (this uses surface normals, not actual refractions, but it's faster this way)
for(i=0; i<geoRes; i++){
for(j=0; j<geoRes; j++){
makeIndices(i, &xminus, &xplus);
xz[i][j][0] = (y[k][xplus][j] - y[k][xminus][j]) * recvert * (depth + y[k][i][j]);
makeIndices(j, &zminus, &zplus);
xz[i][j][1] = (y[k][i][zplus] - y[k][i][zminus]) * recvert * (depth + y[k][i][j]);
}
}
// copy offsets to edges of xz array
for(i=0; i<geoRes; i++){
xz[i][geoRes][0] = xz[i][0][0];
xz[i][geoRes][1] = xz[i][0][1];
}
for(j=0; j<=geoRes; j++){
xz[geoRes][j][0] = xz[0][j][0];
xz[geoRes][j][1] = xz[0][j][1];
}
// compute light intensities
float space = 1.0f / float(geoRes);
for(i=0; i<geoRes; i++){
for(j=0; j<geoRes; j++){
makeIndices(i, &xminus, &xplus);
makeIndices(j, &zminus, &zplus);
// this assumes nominal light intensity is 0.25
intensity[i][j] = (1.0f / (float(geoRes) * float(geoRes)))
/ ((myFabs(xz[xplus][j][0] - xz[i][j][0] + space)
+ myFabs(xz[i][j][0] - xz[xminus][j][0] + space))
* (myFabs(xz[i][zplus][1] - xz[i][j][1] + space)
+ myFabs(xz[i][j][1] - xz[i][zminus][1] + space)))
- 0.125f;
if(intensity[i][j] > 1.0f)
intensity[i][j] = 1.0f;
}
}
// copy intensities to edges of intensity array
for(i=0; i<geoRes; i++)
intensity[i][geoRes] = intensity[i][0];
for(j=0; j<=geoRes; j++)
intensity[geoRes][j] = intensity[0][j];
// draw texture
glClear(GL_COLOR_BUFFER_BIT);
// draw most of texture
draw(0, geoRes, 0, geoRes);
// draw edges of texture that wrap around from opposite sides
int numRows = geoRes / 10;
glPushMatrix();
glTranslatef(-1.0f, 0.0f, 0.0f);
draw(geoRes - numRows, geoRes, 0, geoRes);
glPopMatrix();
glPushMatrix();
glTranslatef(1.0f, 0.0f, 0.0f);
draw(0, numRows, 0, geoRes);
glPopMatrix();
glPushMatrix();
glTranslatef(0.0f, 0.0f, -1.0f);
draw(0, geoRes, geoRes - numRows, geoRes);
glPopMatrix();
glPushMatrix();
glTranslatef(0.0f, 0.0f, 1.0f);
draw(0, geoRes, 0, numRows);
glPopMatrix();
// draw corners too
glPushMatrix();
glTranslatef(-1.0f, 0.0f, -1.0f);
draw(geoRes - numRows, geoRes, geoRes - numRows, geoRes);
glPopMatrix();
glPushMatrix();
glTranslatef(1.0f, 0.0f, -1.0f);
draw(0, numRows, geoRes - numRows, geoRes);
glPopMatrix();
glPushMatrix();
glTranslatef(-1.0f, 0.0f, 1.0f);
draw(geoRes - numRows, geoRes, 0, numRows);
glPopMatrix();
glPushMatrix();
glTranslatef(1.0f, 0.0f, 1.0f);
draw(0, numRows, 0, numRows);
glPopMatrix();
// read back texture
glReadPixels(0, 0, texSize, texSize, GL_RGB, GL_UNSIGNED_BYTE, bitmap);
// create texture object
glBindTexture(GL_TEXTURE_2D, caustictex[k]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, texSize, texSize, GL_RGB,
GL_UNSIGNED_BYTE, bitmap);
#ifdef WIN32
wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE);
#endif
#ifdef RS_XSCREENSAVER
glXSwapBuffers(xdisplay, xwindow);
#endif
}
// restore matrix stack
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
delete[] x;
delete[] y;
delete[] z;
delete[] xz;
delete[] intensity;
delete[] bitmap;
}
void causticTextures::makeFractalAltitudes(){
int i, j, k, a;
float keySeparation = float(numFrames) / float(numKeys);
int* keyFrame = new int[numKeys+1];
// generate keyframes
float phase = 0.0f;
for(k=0; k<numKeys; k++){
keyFrame[k] = int(float(k) * keySeparation);
// set all keyframe altitudes to 0.0
for(i=0; i<geoRes; i++){
for(j=0; j<geoRes; j++)
y[keyFrame[k]][i][j] = 0.0f;
}
// generate altitudes in first positions
phase = float(k) * RS_PIx2 / float(numKeys);
y[keyFrame[k]][0][0] = waveAmp * rsCosf(1.5707f + phase);
y[keyFrame[k]][geoRes/2][0] = waveAmp * rsCosf(1.5707f + phase);
y[keyFrame[k]][geoRes/2][geoRes/2] = waveAmp * rsCosf(3.1416f + phase);
y[keyFrame[k]][0][geoRes/2] = waveAmp * rsCosf(4.7124f + phase);
// recurse to find remaining altitudes
altitudeSquare(0, geoRes/2, 0, geoRes/2, y[keyFrame[k]]);
altitudeSquare(geoRes/2, geoRes, 0, geoRes/2, y[keyFrame[k]]);
altitudeSquare(0, geoRes/2, geoRes/2, geoRes, y[keyFrame[k]]);
altitudeSquare(geoRes/2, geoRes, geoRes/2, geoRes, y[keyFrame[k]]);
}
// interpolate to find remaining frames
int diff, kk;
int kf0, kf1, kf2, kf3; // keyframe indices
float where;
keyFrame[numKeys] = numFrames;
for(k=0; k<numKeys; k++){
kf1 = keyFrame[k];
kf2 = keyFrame[k+1];
diff = kf2 - kf1;
if(kf2 == numFrames)
kf2 = 0;
kk = k - 1;
if(kk < 0)
kk = numKeys - 1;
kf0 = keyFrame[kk];
kk = k + 2;
if(kk >= numKeys)
kk -= numKeys;
kf3 = keyFrame[kk];
if(diff > 1){
for(a=kf1+1; a<kf1+diff; a++){
where = float(a-kf1) / float(diff);
for(i=0; i<geoRes; i++)
for(j=0; j<geoRes; j++)
y[a][i][j] = interpolate(y[kf0][i][j], y[kf1][i][j],
y[kf2][i][j], y[kf3][i][j], where);
}
}
}
delete[] keyFrame;
}
void causticTextures::altitudeSquare(int left, int right, int bottom, int top, float** alt){
// find wrapped indices
int rr = right;
if(rr == geoRes)
rr = 0;
int tt = top;
if(tt == geoRes)
tt = 0;
// for determining if there is a gap to be filled
int hor = right - left;
int vert = top - bottom;
int centerHor = (left + right) / 2;
int centerVert = (bottom + top) / 2;
float offset;
if(hor > 1){ // find bottom and top altitudes
offset = myFabs(waveAmp * float(x[right] - x[left]));
if(alt[centerHor][bottom] == 0.0f)
alt[centerHor][bottom] = (alt[left][bottom] + alt[rr][bottom]) * 0.5f
+ rsRandf(offset+offset) - offset;
if(alt[centerHor][tt] == 0.0f)
alt[centerHor][tt] = (alt[left][tt] + alt[rr][tt]) * 0.5f
+ rsRandf(offset+offset) - offset;
}
if(vert > 1){ // find left and right altitudes
offset = myFabs(waveAmp * float(z[top] - z[bottom]));
if(alt[left][centerVert] == 0.0f)
alt[left][centerVert] = (alt[left][bottom] + alt[left][tt]) * 0.5f
+ rsRandf(offset+offset) - offset;
if(alt[rr][centerVert] == 0.0f)
alt[rr][centerVert] = (alt[rr][bottom] + alt[rr][tt]) * 0.5f
+ rsRandf(offset+offset) - offset;
}
if(hor > 1 && vert > 1){ // find center altitude
offset = waveAmp * 0.5f *
(myFabs(float(x[right] - x[left]))
+ myFabs(float(z[top] - z[bottom])));
alt[centerHor][centerVert] = (alt[left][bottom] + alt[rr][bottom] + alt[left][tt]
+ alt[rr][tt]) * 0.25f + rsRandf(offset+offset) - offset;
}
// keep recursing if necessary
int quadrant[4] = {0, 0, 0, 0};
if(centerHor - left > 1){
quadrant[0] ++;
quadrant[2] ++;
}
if(right - centerHor > 1){
quadrant[1] ++;
quadrant[2] ++;
}
if(centerVert - bottom > 1){
quadrant[0] ++;
quadrant[1] ++;
}
if(top - centerVert > 1){
quadrant[2] ++;
quadrant[3] ++;
}
if(quadrant[0])
altitudeSquare(left, centerHor, bottom, centerVert, alt);
if(quadrant[1])
altitudeSquare(centerHor, right, bottom, centerVert, alt);
if(quadrant[2])
altitudeSquare(left, centerHor, centerVert, top, alt);
if(quadrant[3])
altitudeSquare(centerHor, right, centerVert, top, alt);
return;
}
void causticTextures::makeTrigAltitudes(){
int i, j, k;
float xx, zz, offset;
for(k=0; k<numFrames; k++){
offset = RS_PIx2 * float(k) / float(numFrames);
for(i=0; i<geoRes; i++){
xx = RS_PIx2 * float(i) / float(geoRes);
for(j=0; j<geoRes; j++){
zz = RS_PIx2 * float(j) / float(geoRes);
/*y[k][i][j] = waveAmp
* (0.12f * rsCosf(xx + 2.0f * offset)
+ 0.08f * rsCosf(-1.0f * xx + 2.0f * zz + offset)
+ 0.04f * rsCosf(-2.0f * xx - 4.0f * zz + offset)
+ 0.014f * rsCosf(xx - 7.0f * zz - 2.0f * offset)
+ 0.014f * rsCosf(3.0f * xx + 5.0f * zz + offset)
+ 0.014f * rsCosf(9.0f * xx + zz - offset)
+ 0.007f * rsCosf(11.0f * xx + 7.0f * zz - offset)
+ 0.007f * rsCosf(4.0f * xx - 13.0f * zz + offset)
+ 0.007f * rsCosf(19.0f * xx - 9.0f * zz - offset));*/
y[k][i][j] = waveAmp
* (0.08f * rsCosf(xx * 2.0f + offset)
+ 0.06f * rsCosf(-1.0f * xx + 2.0f * zz + offset)
+ 0.04f * rsCosf(-2.0f * xx - 3.0f * zz + offset)
+ 0.01f * rsCosf(xx - 7.0f * zz - 2.0f * offset)
+ 0.01f * rsCosf(3.0f * xx + 5.0f * zz + offset)
+ 0.01f * rsCosf(9.0f * xx + zz - offset)
+ 0.005f * rsCosf(11.0f * xx + 7.0f * zz - offset)
+ 0.005f * rsCosf(4.0f * xx - 13.0f * zz + offset)
+ 0.003f * rsCosf(19.0f * xx - 9.0f * zz - offset));
}
}
}
}
void causticTextures::draw(int xlo, int xhi, int zlo, int zhi){
int i, j;
float mult;
for(j=zlo; j<zhi; j++){
// red
mult = 1.0f - refractionMult / float(geoRes);
glBegin(GL_TRIANGLE_STRIP);
for(i=xlo; i<=xhi; i++){
glColor3f(intensity[i][j+1], 0.0f, 0.0f);
glVertex3f(x[i] + xz[i][j+1][0] * mult, 0.0f, z[j+1] + xz[i][j+1][1] * mult);
glColor3f(intensity[i][j], 0.0f, 0.0f);
glVertex3f(x[i] + xz[i][j][0] * mult, 0.0f, z[j] + xz[i][j][1] * mult);
}
glEnd();
// green
glBegin(GL_TRIANGLE_STRIP);
for(i=xlo; i<=xhi; i++){
glColor3f(0.0f, intensity[i][j+1], 0.0f);
glVertex3f(x[i] + xz[i][j+1][0], 0.0f, z[j+1] + xz[i][j+1][1]);
glColor3f(0.0f, intensity[i][j], 0.0f);
glVertex3f(x[i] + xz[i][j][0], 0.0f, z[j] + xz[i][j][1]);
}
glEnd();
// blue
mult = 1.0f + refractionMult / float(geoRes);
glBegin(GL_TRIANGLE_STRIP);
for(i=xlo; i<=xhi; i++){
glColor3f(0.0f, 0.0f, intensity[i][j+1]);
glVertex3f(x[i] + xz[i][j+1][0] * mult, 0.0f, z[j+1] + xz[i][j+1][1] * mult);
glColor3f(0.0f, 0.0f, intensity[i][j]);
glVertex3f(x[i] + xz[i][j][0] * mult, 0.0f, z[j] + xz[i][j][1] * mult);
}
glEnd();
}
}
void causticTextures::makeIndices(int index, int* minus, int* plus){
*minus = index - 1;
if(*minus < 0)
*minus = geoRes - 1;
*plus = index + 1;
if(*plus >= geoRes)
*plus = 0;
}
// Here's a little calculus that takes 4 points along a single
// dimension and interpolates smoothly between the second and third
// depending on the value of where which can be 0.0 to 1.0.
// The slope at b is estimated using a and c. The slope at c
// is estimated using b and d.
float causticTextures::interpolate(float a, float b, float c, float d, float where){
float q, r, s, t;
q = (((3.0f * b) + d - a - (3.0f * c)) * (where * where * where)) * 0.5f;
r = (((2.0f * a) - (5.0f * b) + (4.0f * c) - d) * (where * where)) * 0.5f;
s = ((c - a) * where) * 0.5f;
t = b;
return(q + r + s + t);
}
@@ -0,0 +1,72 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef CAUSTICS_H
#define CAUSTICS_H
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
class causticTextures{
public:
int numKeys;
int numFrames;
int geoRes;
int texSize;
float waveAmp;
float refractionMult;
// textures indices for OpenGL texture objects
GLuint* caustictex;
// space for storing geometry of water surface
float* x; // x and z are the same for each frame
float* z;
float*** y; // y (altitude) is different
float*** xz; // projected vertex positions
float** intensity; // projected light intensity
GLubyte** bitmap;
// constructor takes the following parameters:
// num keyframes (only for fractal heightfield generation),
// number of frames, geometry resolution, texture resolution, water depth, wave amplitude,
// refraction multiplier (sort of like index of refraction)
causticTextures(int keys, int frames, int res, int size, float depth, float wa, float rm);
~causticTextures(){}
private:
void makeFractalAltitudes();
void makeTrigAltitudes();
void altitudeSquare(int left, int right, int bottom, int top, float** alt);
void draw(int xlo, int xhi, int zlo, int zhi);
void makeIndices(int index, int* minus, int* plus);
float myFabs(float x){if(x<0) return -x; return x;};
float interpolate(float a, float b, float c, float d, float where);
};
#endif
@@ -0,0 +1,92 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "extensions.h"
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
PFNGLUNIFORM1IARBPROC glUniform1iARB;
int queryExtension(char* name){
char* extensions = (char *)glGetString(GL_EXTENSIONS);
char* start = extensions;
char* position;
char* end;
int quit = 0;
while(quit == 0){
position = strstr(start, name);
if(!position)
quit = 1;
else{
// is name a substring of a larger name?
end = position + strlen(name);
if(position == start || *(position-1) == ' '){
if(*end == ' ' || *end == '\0')
return 1;
}
start = end;
}
}
return 0;
}
void* getProcAddr(char* name){
#ifdef WIN32
void *addr = (void *)wglGetProcAddress(name);
return addr;
#else
void *addr = (void *)glXGetProcAddressARB((char *)name);
return addr;
#endif
}
int initExtensions(){
if(queryExtension("GL_ARB_multitexture") && queryExtension("GL_ARB_texture_cube_map") && queryExtension("GL_ARB_shader_objects")){
glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)getProcAddr("glActiveTextureARB");
glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)getProcAddr("glCreateShaderObjectARB");
glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)getProcAddr("glShaderSourceARB");
glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)getProcAddr("glCompileShaderARB");
glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC)getProcAddr("glCreateProgramObjectARB");
glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC)getProcAddr("glAttachObjectARB");
glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC)getProcAddr("glLinkProgramARB");
glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC)getProcAddr("glUseProgramObjectARB");
glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC)getProcAddr("glGetUniformLocationARB");
glUniform1iARB = (PFNGLUNIFORM1IARBPROC)getProcAddr("glUniform1iARB");
return 1;
}
return 0;
}
@@ -0,0 +1,49 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef EXTENSIONS_H
#define EXTENSIONS_H
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/glext.h>
extern PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
extern PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
extern PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObjectARB;
extern PFNGLATTACHOBJECTARBPROC glAttachObjectARB;
extern PFNGLLINKPROGRAMARBPROC glLinkProgramARB;
extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObjectARB;
extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocationARB;
extern PFNGLUNIFORM1IARBPROC glUniform1iARB;
int initExtensions();
#endif
@@ -0,0 +1,345 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "flare.h"
#define FLARESIZE 64
unsigned char flare1[FLARESIZE][FLARESIZE][4];
unsigned char flare2[FLARESIZE][FLARESIZE][4];
unsigned char flare3[FLARESIZE][FLARESIZE][4];
unsigned char flare4[FLARESIZE][FLARESIZE][4];
unsigned int flarelist[4];
unsigned int flaretex[4];
extern int xsize, ysize;
extern float aspectRatio;
extern float billboardMat[16];
extern double modelMat[16];
extern double projMat[16];
extern int viewport[4];
// Calculated in main draw routine each frame
extern float frameTime;
extern float camPos[3];
void initFlares(){
int i, j;
float x, y;
float temp;
glGenTextures(4, flaretex);
// First flare: basic sphere
for(i=0; i<FLARESIZE; i++){
for(j=0; j<FLARESIZE; j++){
flare1[i][j][0] = 255;
flare1[i][j][1] = 255;
flare1[i][j][2] = 255;
x = float(i - FLARESIZE / 2) / float(FLARESIZE / 2);
y = float(j - FLARESIZE / 2) / float(FLARESIZE / 2);
temp = 1.0f - ((x * x) + (y * y));
if(temp > 1.0f)
temp = 1.0f;
if(temp < 0.0f)
temp = 0.0f;
temp = temp * temp;
flare1[i][j][3] = (unsigned char)(255.0f * temp);
}
}
glBindTexture(GL_TEXTURE_2D, flaretex[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 4, FLARESIZE, FLARESIZE, 0, GL_RGBA,
GL_UNSIGNED_BYTE, flare1);
// Second flare: flattened sphere
for(i=0; i<FLARESIZE; i++){
for(j=0; j<FLARESIZE; j++){
flare2[i][j][0] = 255;
flare2[i][j][1] = 255;
flare2[i][j][2] = 255;
x = float(i - FLARESIZE / 2) / float(FLARESIZE / 2);
y = float(j - FLARESIZE / 2) / float(FLARESIZE / 2);
temp = 2.5f * (1.0f - ((x * x) + (y * y)));
if(temp > 1.0f)
temp = 1.0f;
if(temp < 0.0f)
temp = 0.0f;
//temp = temp * temp * temp * temp;
flare2[i][j][3] = (unsigned char)(255.0f * temp);
}
}
glBindTexture(GL_TEXTURE_2D, flaretex[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 4, FLARESIZE, FLARESIZE, 0, GL_RGBA,
GL_UNSIGNED_BYTE, flare2);
// Third flare: torus
for(i=0; i<FLARESIZE; i++){
for(j=0; j<FLARESIZE; j++){
flare3[i][j][0] = 255;
flare3[i][j][1] = 255;
flare3[i][j][2] = 255;
x = float(i - FLARESIZE / 2) / float(FLARESIZE / 2);
y = float(j - FLARESIZE / 2) / float(FLARESIZE / 2);
temp = 4.0f * ((x * x) + (y * y)) * (1.0f - ((x * x) + (y * y)));
if(temp > 1.0f)
temp = 1.0f;
if(temp < 0.0f)
temp = 0.0f;
temp = temp * temp * temp * temp;
flare3[i][j][3] = (unsigned char)(255.0f * temp);
}
}
glBindTexture(GL_TEXTURE_2D, flaretex[2]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 4, FLARESIZE, FLARESIZE, 0, GL_RGBA,
GL_UNSIGNED_BYTE, flare3);
// Fourth flare: weird flare
for(i=0; i<FLARESIZE; i++){
for(j=0; j<FLARESIZE; j++){
x = float(i - FLARESIZE / 2) / float(FLARESIZE / 2);
if(x < 0.0f)
x = -x;
y = float(j - FLARESIZE / 2) / float(FLARESIZE / 2);
if(y < 0.0f)
y = -y;
flare4[i][j][0] = 255;
flare4[i][j][1] = 255;
temp = 0.14f * (1.0f - max(x, y)) / max((x * y), 0.05f);
if(temp > 1.0f)
temp = 1.0f;
if(temp < 0.0f)
temp = 0.0f;
flare4[i][j][2] = (unsigned char)(255.0f * temp);
temp = 0.1f * (1.0f - max(x, y)) / max((x * y), 0.1f);
if(temp > 1.0f)
temp = 1.0f;
if(temp < 0.0f)
temp = 0.0f;
flare4[i][j][3] = (unsigned char)(255.0f * temp);
}
}
glBindTexture(GL_TEXTURE_2D, flaretex[3]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 4, FLARESIZE, FLARESIZE, 0, GL_RGBA,
GL_UNSIGNED_BYTE, flare4);
// Build display lists
flarelist[0] = glGenLists(4);
flarelist[1] = flarelist[0] + 1;
flarelist[2] = flarelist[0] + 2;
flarelist[3] = flarelist[0] + 3;
for(i=0; i<4; i++){
glNewList(flarelist[i], GL_COMPILE);
glBindTexture(GL_TEXTURE_2D, flaretex[i]);
glBegin(GL_TRIANGLE_STRIP);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-0.5f, -0.5f, 0.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(0.5f, -0.5f, 0.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-0.5f, 0.5f, 0.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(0.5f, 0.5f, 0.0f);
glEnd();
glEndList();
}
}
// Draw a flare at a specified (x,y) location on the screen
// Screen corners are at (0,0) and (1,1)
// alpha = 0.0 for lowest intensity; alpha = 1.0 for highest intensity
void flare(double *pos, float red, float green, float blue, float alpha){
double winx, winy, winz; // in screen coordinates
float x, y, dx, dy;
float fadewidth, temp;
static float flicker = 0.95f;
gluProject(pos[0], pos[1], pos[2],
modelMat, projMat, viewport,
&winx, &winy, &winz);
x = (float(winx) / float(xsize)) * aspectRatio;
y = float(winy) / float(ysize);
float diff[3] = {float(pos[0] - camPos[0]), float(pos[1] - camPos[1]), float(pos[2] - camPos[2])};
if(diff[0] * billboardMat[8] + diff[1] * billboardMat[9] + diff[2] * billboardMat[10] > 0.0f)
return;
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glEnable(GL_BLEND);
glEnable(GL_TEXTURE_2D);
glDisable(GL_LIGHTING);
// Fade alpha if source is off edge of screen
fadewidth = float(xsize) / 10.0f;
if(y < 0){
temp = fadewidth + y;
if(temp < 0.0f)
return;
alpha *= temp / fadewidth;
}
if(y > ysize){
temp = fadewidth - y + ysize;
if(temp < 0.0f)
return;
alpha *= temp / fadewidth;
}
if(x < 0){
temp = fadewidth + x;
if(temp < 0.0f)
return;
alpha *= temp / fadewidth;
}
if(x > xsize){
temp = fadewidth - x + xsize;
if(temp < 0.0f)
return;
alpha *= temp / fadewidth;
}
// Find lens flare vector
// This vector runs from the light source through the screen's center
dx = 0.5f * aspectRatio - x;
dy = 0.5f - y;
// Setup projection matrix
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
gluOrtho2D(0, aspectRatio, 0, 1.0f);
// Update fractal flickering
flicker += frameTime * (rsRandf(2.0f) - 1.0f);
if(flicker < 0.9f)
flicker = 0.9f;
if(flicker > 1.1f)
flicker = 1.1f;
alpha *= flicker;
// Draw stuff
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glLoadIdentity();
glTranslatef(x, y, 0.0f);
glScalef(0.1f * flicker, 0.1f * flicker, 1.0f);
glColor4f(red, green, blue * 0.8f, alpha);
glCallList(flarelist[0]);
// wide flare
glLoadIdentity();
glTranslatef(x, y, 0.0f);
glScalef(5.0f * alpha, 0.05f * alpha, 1.0f);
glColor4f(red * 0.3f, green * 0.3f, blue, alpha);
glCallList(flarelist[0]);
// torus
glLoadIdentity();
glTranslatef(x, y, 0.0f);
glScalef(0.5f, 0.2f, 1.0f);
glColor4f(red, green * 0.5f, blue * 0.5f, alpha * 0.4f);
glCallList(flarelist[2]);
// 3 blueish dots
glLoadIdentity();
glTranslatef(x + dx * 0.35f, y + dy * 0.35f, 0.0f);
glScalef(0.06f, 0.06f, 1.0f);
glColor4f(red * 0.85f, green * 0.85f, blue, alpha * 0.5f);
glCallList(flarelist[1]);
glLoadIdentity();
glTranslatef(x + dx * 0.45f, y + dy * 0.45f, 0.0f);
glScalef(0.09f, 0.09f, 1.0f);
glColor4f(red * 0.7f, green * 0.7f, blue, alpha * 0.4f);
glCallList(flarelist[1]);
glLoadIdentity();
glTranslatef(x + dx * 0.55f, y + dy * 0.55f, 0.0f);
glScalef(0.12f, 0.12f, 1.0f);
glColor4f(red * 0.55f, green * 0.55f, blue, alpha * 0.3f);
glCallList(flarelist[1]);
// 4 more dots
glLoadIdentity();
glTranslatef(x + dx * 0.75f, y + dy * 0.75f, 0.0f);
glScalef(0.14f, 0.07f, 1.0f);
glColor4f(red * 0.3f, green * 0.3f, blue * 0.3f, alpha);
glCallList(flarelist[3]);
glLoadIdentity();
glTranslatef(x + dx * 0.78f, y + dy * 0.78f, 0.0f);
glScalef(0.06f, 0.06f, 1.0f);
glColor4f(red * 0.3f, green * 0.4f, blue * 0.4f, alpha * 0.5f);
glCallList(flarelist[1]);
glLoadIdentity();
glTranslatef(x + dx * 1.25f, y + dy * 1.25f, 0.0f);
glScalef(0.1f, 0.1f, 1.0f);
glColor4f(red * 0.3f, green * 0.4f, blue * 0.3f, alpha * 0.5f);
glCallList(flarelist[1]);
glLoadIdentity();
glTranslatef(x + dx * 1.3f, y + dy * 1.3f, 0.0f);
glScalef(0.07f, 0.07f, 1.0f);
glColor4f(red * 0.6f, green * 0.45f, blue * 0.3f, alpha * 0.5f);
glCallList(flarelist[1]);
// stretched weird flare
glLoadIdentity();
glTranslatef(x + dx * 1.45f, y + dy * 1.45f, 0.0f);
glScalef(0.8f, 0.2f, 1.0f);
glRotatef(x * 70.0f, 0, 0, 1);
glColor4f(red, green, blue, alpha * 0.4f);
glCallList(flarelist[3]);
// circle
glLoadIdentity();
glTranslatef(x + dx * 2.0f, y + dy * 2.0f, 0.0f);
glScalef(0.3f, 0.3f, 1.0f);
glColor4f(red, green, blue, alpha * 0.2f);
glCallList(flarelist[1]);
// big weird flare
glLoadIdentity();
glTranslatef(x + dx * 2.4f, y + dy * 2.4f, 0.0f);
glRotatef(y * 40.0f, 0, 0, 1);
glScalef(0.7f, 0.7f, 1.0f);
glColor4f(red, green, blue, alpha * 0.3f);
glCallList(flarelist[3]);
// Unsetup projection matrix
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}
@@ -0,0 +1,57 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef FLARE_H
#define FLARE_H
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
#include <GL/glu.h>
#include <rsMath/rsMath.h>
// Externed so flares can be drawn elsewhere
extern unsigned int flarelist[4];
extern unsigned int flaretex[4];
// Generate textures for lens flares
// then applies textures to geometry in display lists
void initFlares();
// Draw a flare at a specified (x,y) location on the screen
// Screen corners are at (0,0) and (1,1)
// alpha = 0.0 for lowest intensity; alpha = 1.0 for highest intensity
void flare(double *pos, float red, float green, float blue, float alpha);
#ifndef WIN32
inline const float max(const float& a, const float& b){ return (a > b) ? a : b; }
#endif
#endif // FLARE_H
@@ -0,0 +1,167 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "goo.h"
extern float frameTime, simulationTime;
goo::goo(int res, float rad){
int i, j;
for(i=0; i<4; ++i){
cp[i] = float(i);
cs[i] = 0.1f + rsRandf(0.4f);
}
volumeSize = 2.0f;
if(res < 5)
res = 5;
resolution = res;
radius = rad;
unitSize = volumeSize / float(res);
arraySize = 2 * int(0.99f + radius / volumeSize);
// Init implicit surfaces
volume = new impCubeVolume;
volume->init(resolution, resolution, resolution, unitSize);
// Using exact normals instead of fast normals. This should be slower, but it is faster
// in this case because the surface function is so ridiculously fast.
volume->useFastNormals(true);
volume->setCrawlFromSides(true);
volume->function = function;
volume->setSurfaceValue(0.4f);
surface = new impSurface**[arraySize];
useSurface = new bool*[arraySize];
for(i=0; i<arraySize; i++){
surface[i] = new impSurface*[arraySize];
useSurface[i] = new bool[arraySize];
for(j=0; j<arraySize; j++){
surface[i][j] = new impSurface;
useSurface[i][j] = false;
}
}
}
goo::~goo(){
delete[] surface;
delete[] useSurface;
delete volume;
}
void goo::update(float x, float z, float heading, float fov){
int i, j;
// update goo function constants
for(i=0; i<4; i++){
cp[i] += cs[i] * frameTime;
if(cp[i] >= RS_PIx2)
cp[i] -= RS_PIx2;
c[i] = 0.25f * cosf(cp[i]);
}
float halfFov = 0.5f * fov;
camx = x;
camz = z;
centerx = unitSize * float(int(0.5f + x / unitSize));
centerz = unitSize * float(int(0.5f + z / unitSize));
clip[0][0] = cosf(heading + halfFov);
clip[0][1] = -sinf(heading + halfFov);
clip[1][0] = -cosf(heading - halfFov);
clip[1][1] = sinf(heading - halfFov);
clip[2][0] = sinf(heading);
clip[2][1] = -cosf(heading);
// Empty crawl point vector.
// This is only needed so that the right version of impCubeVolume::makeSurface() is called.
impCrawlPointVector cpv;
for(i=0; i<arraySize; i++){
for(j=0; j<arraySize; j++){
shiftx = volumeSize * (0.5f + float(i - arraySize / 2));
shiftz = volumeSize * (0.5f + float(j - arraySize / 2));
if(shiftx * clip[0][0] + shiftz * clip[0][1] > volumeSize * -1.41421f){
if(shiftx * clip[1][0] + shiftz * clip[1][1] > volumeSize * -1.41421f){
if(shiftx * clip[2][0] + shiftz * clip[2][1] < radius + volumeSize * 1.41421f){
shiftx += centerx;
shiftz += centerz;
volume->setSurface(surface[i][j]);
surface[i][j]->reset();
volume->makeSurface(cpv);
useSurface[i][j] = true;
}
}
}
}
}
}
float goo::c[4];
float goo::shiftx;
float goo::shiftz;
float goo::camx;
float goo::camz;
float goo::function(float* position){
const float px(position[0] + shiftx);
const float pz(position[2] + shiftz);
const float x(px - camx);
const float z(pz - camz);
return
// This first term defines upper and lower surfaces.
position[1] * position[1] * 1.25f
// These terms make the surfaces wavy.
+ c[0] * rsCosf(px - 2.71f * position[1])
+ c[1] * rsCosf(4.21f * position[1] + pz)
+ c[2] * rsCosf(1.91f * px - 1.67f * pz)
+ c[3] * rsCosf(1.53f * px + 1.11f * position[1] + 2.11f * pz)
// The last term creates a bubble around the eyepoint so it doesn't
// punch through the surface.
- 0.1f / (x * x + position[1] * position[1] + z * z);
}
void goo::draw(){
int i, j;
for(i=0; i<arraySize; i++){
for(j=0; j<arraySize; j++){
if(useSurface[i][j]){
shiftx = centerx + volumeSize * (0.5f + float(i - arraySize / 2));
shiftz = centerz + volumeSize * (0.5f + float(j - arraySize / 2));
glPushMatrix();
glTranslatef(shiftx, 0.0f, shiftz);
surface[i][j]->draw();
glPopMatrix();
useSurface[i][j] = false;
}
}
}
}
@@ -0,0 +1,71 @@
/*
* Copyright (C) 2005-2010 Terence M. Welsh
*
* This file is part of Hyperspace.
*
* Hyperspace is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* Hyperspace is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef GOO_H
#define GOO_H
#ifdef WIN32
#include <windows.h>
#endif
#include <GL/gl.h>
#include <rsMath/rsMath.h>
#include <list>
#include <Implicit/impCubeVolume.h>
#include <Implicit/impCrawlPoint.h>
class goo{
public:
int resolution;
float radius;
float unitSize;
float volumeSize;
static float c[4]; // constants
float cp[4]; // constants phase
float cs[4]; // constants speed
static float camx, camz;
float centerx, centerz;
static float shiftx, shiftz;
int arraySize;
impCubeVolume* volume;
impSurface*** surface;
bool** useSurface;
// normals of planes for culling impSurfaces
float clip[3][2];
goo(int res, float rad);
~goo();
void update(float x, float z, float heading, float fov);
static float function(float* position);
void draw();
};
#endif

Some files were not shown because too many files have changed in this diff Show More