Arbeiten aus dem URlaub
1
simarboreal/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/.gradle/
|
||||
332
simarboreal/assets/MatDefs/MultiResolution.frag
Normal file
@@ -0,0 +1,332 @@
|
||||
#import "Common/ShaderLib/Parallax.glsllib"
|
||||
#import "Common/ShaderLib/Optics.glsllib"
|
||||
#define ATTENUATION
|
||||
//#define HQ_ATTENUATION
|
||||
|
||||
#import "MatDefs/FragScattering.glsllib"
|
||||
|
||||
varying vec2 texCoord;
|
||||
#ifdef SEPARATE_TEXCOORD
|
||||
varying vec2 texCoord2;
|
||||
#endif
|
||||
|
||||
varying vec3 AmbientSum;
|
||||
varying vec4 DiffuseSum;
|
||||
varying vec3 SpecularSum;
|
||||
|
||||
varying float z;
|
||||
|
||||
#ifndef VERTEX_LIGHTING
|
||||
uniform vec4 g_LightDirection;
|
||||
//varying vec3 vPosition;
|
||||
varying vec3 vViewDir;
|
||||
varying vec4 vLightDir;
|
||||
varying vec3 lightVec;
|
||||
#else
|
||||
varying vec2 vertexLightValues;
|
||||
#endif
|
||||
|
||||
#ifdef DIFFUSEMAP
|
||||
uniform sampler2D m_DiffuseMap;
|
||||
uniform sampler2D m_BackgroundDiffuseMap;
|
||||
uniform sampler2D m_NoiseMap;
|
||||
#endif
|
||||
|
||||
#ifdef SPECULARMAP
|
||||
uniform sampler2D m_SpecularMap;
|
||||
#endif
|
||||
|
||||
#ifdef PARALLAXMAP
|
||||
uniform sampler2D m_ParallaxMap;
|
||||
#endif
|
||||
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
|
||||
uniform float m_ParallaxHeight;
|
||||
#endif
|
||||
|
||||
#ifdef LIGHTMAP
|
||||
uniform sampler2D m_LightMap;
|
||||
#endif
|
||||
|
||||
#ifdef NORMALMAP
|
||||
uniform sampler2D m_NormalMap;
|
||||
#else
|
||||
varying vec3 vNormal;
|
||||
#endif
|
||||
|
||||
#ifdef ALPHAMAP
|
||||
uniform sampler2D m_AlphaMap;
|
||||
#endif
|
||||
|
||||
#ifdef COLORRAMP
|
||||
uniform sampler2D m_ColorRamp;
|
||||
#endif
|
||||
|
||||
uniform float m_AlphaDiscardThreshold;
|
||||
|
||||
#ifndef VERTEX_LIGHTING
|
||||
uniform float m_Shininess;
|
||||
|
||||
#ifdef HQ_ATTENUATION
|
||||
uniform vec4 g_LightPosition;
|
||||
#endif
|
||||
|
||||
#ifdef USE_REFLECTION
|
||||
uniform float m_ReflectionPower;
|
||||
uniform float m_ReflectionIntensity;
|
||||
varying vec4 refVec;
|
||||
|
||||
uniform ENVMAP m_EnvMap;
|
||||
#endif
|
||||
|
||||
float tangDot(in vec3 v1, in vec3 v2){
|
||||
float d = dot(v1,v2);
|
||||
#ifdef V_TANGENT
|
||||
d = 1.0 - d*d;
|
||||
return step(0.0, d) * sqrt(d);
|
||||
#else
|
||||
return d;
|
||||
#endif
|
||||
}
|
||||
|
||||
float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){
|
||||
#ifdef MINNAERT
|
||||
float NdotL = max(0.0, dot(norm, lightdir));
|
||||
float NdotV = max(0.0, dot(norm, viewdir));
|
||||
return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5;
|
||||
#else
|
||||
return max(0.0, dot(norm, lightdir));
|
||||
#endif
|
||||
}
|
||||
|
||||
float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
|
||||
// NOTE: check for shiny <= 1 removed since shininess is now
|
||||
// 1.0 by default (uses matdefs default vals)
|
||||
#ifdef LOW_QUALITY
|
||||
// Blinn-Phong
|
||||
// Note: preferably, H should be computed in the vertex shader
|
||||
vec3 H = (viewdir + lightdir) * vec3(0.5);
|
||||
return pow(max(tangDot(H, norm), 0.0), shiny);
|
||||
#elif defined(WARDISO)
|
||||
// Isotropic Ward
|
||||
vec3 halfVec = normalize(viewdir + lightdir);
|
||||
float NdotH = max(0.001, tangDot(norm, halfVec));
|
||||
float NdotV = max(0.001, tangDot(norm, viewdir));
|
||||
float NdotL = max(0.001, tangDot(norm, lightdir));
|
||||
float a = tan(acos(NdotH));
|
||||
float p = max(shiny/128.0, 0.001);
|
||||
return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL)));
|
||||
#else
|
||||
// Standard Phong
|
||||
vec3 R = reflect(-lightdir, norm);
|
||||
return pow(max(tangDot(R, viewdir), 0.0), shiny);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec2 computeLighting(in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){
|
||||
float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir);
|
||||
float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess);
|
||||
|
||||
#ifdef HQ_ATTENUATION
|
||||
float att = clamp(1.0 - g_LightPosition.w * length(lightVec), 0.0, 1.0);
|
||||
#else
|
||||
float att = vLightDir.w;
|
||||
#endif
|
||||
|
||||
if (m_Shininess <= 1.0) {
|
||||
specularFactor = 0.0; // should be one instruction on most cards ..
|
||||
}
|
||||
|
||||
specularFactor *= diffuseFactor;
|
||||
|
||||
return vec2(diffuseFactor, specularFactor) * vec2(att);
|
||||
}
|
||||
#endif
|
||||
|
||||
vec4 getColor( in sampler2D diffuseMap, in sampler2D diffuseMap2,
|
||||
in sampler2D normalMap, in vec2 tc, in float distMix,
|
||||
out vec3 normal ) {
|
||||
|
||||
vec2 tcOffset;
|
||||
tcOffset = texture2D(m_NoiseMap, tc * 0.01).xy * 6.0 - 3.0;
|
||||
vec4 diffuseColor = texture2D(diffuseMap, (tc + tcOffset) * 0.75);
|
||||
|
||||
tcOffset = (texture2D(m_NoiseMap, tc * 0.01).xy * 6.0) - 3.0;
|
||||
vec4 subColor = texture2D(diffuseMap2, ((tc + tcOffset) * 1.0) * 0.1 );
|
||||
diffuseColor = mix(diffuseColor, subColor, distMix);
|
||||
|
||||
#ifdef NORMALMAP
|
||||
vec4 normalHeight = texture2D(normalMap, tc);
|
||||
normal = normalize((normalHeight.xyz * vec3(2.0) - vec3(1.0)));
|
||||
#else
|
||||
normal = vec3(0.0, 1.0, 0.0);
|
||||
#endif
|
||||
|
||||
return diffuseColor;
|
||||
}
|
||||
|
||||
|
||||
void main(){
|
||||
vec2 newTexCoord;
|
||||
|
||||
#if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING)
|
||||
|
||||
#ifdef STEEP_PARALLAX
|
||||
#ifdef NORMALMAP_PARALLAX
|
||||
//parallax map is stored in the alpha channel of the normal map
|
||||
newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
|
||||
#else
|
||||
//parallax map is a texture
|
||||
newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
|
||||
#endif
|
||||
#else
|
||||
#ifdef NORMALMAP_PARALLAX
|
||||
//parallax map is stored in the alpha channel of the normal map
|
||||
newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight);
|
||||
#else
|
||||
//parallax map is a texture
|
||||
newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight);
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
newTexCoord = texCoord;
|
||||
#endif
|
||||
|
||||
float distMix = z / 32.0;
|
||||
distMix = clamp(distMix, 0.4, 1.0);
|
||||
|
||||
#ifdef DIFFUSEMAP
|
||||
vec3 newNormal;
|
||||
#ifdef NORMALMAP
|
||||
vec4 diffuseColor = getColor(m_DiffuseMap, m_BackgroundDiffuseMap,
|
||||
m_NormalMap, texCoord, distMix, newNormal);
|
||||
#else
|
||||
vec4 diffuseColor = getColor(m_DiffuseMap, m_BackgroundDiffuseMap,
|
||||
m_DiffuseMap, texCoord, distMix, newNormal);
|
||||
#endif
|
||||
#else
|
||||
vec4 diffuseColor = vec4(1.0);
|
||||
vec3 newNormal = vec3(0.0, 1.0, 0.0);
|
||||
#endif
|
||||
|
||||
float alpha = DiffuseSum.a * diffuseColor.a;
|
||||
#ifdef ALPHAMAP
|
||||
alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r;
|
||||
#endif
|
||||
if(alpha < m_AlphaDiscardThreshold){
|
||||
discard;
|
||||
}
|
||||
|
||||
#ifndef VERTEX_LIGHTING
|
||||
float spotFallOff = 1.0;
|
||||
|
||||
#if __VERSION__ >= 110
|
||||
// allow use of control flow
|
||||
if(g_LightDirection.w != 0.0){
|
||||
#endif
|
||||
|
||||
vec3 L = normalize(lightVec.xyz);
|
||||
vec3 spotdir = normalize(g_LightDirection.xyz);
|
||||
float curAngleCos = dot(-L, spotdir);
|
||||
float innerAngleCos = floor(g_LightDirection.w) * 0.001;
|
||||
float outerAngleCos = fract(g_LightDirection.w);
|
||||
float innerMinusOuter = innerAngleCos - outerAngleCos;
|
||||
spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter;
|
||||
|
||||
#if __VERSION__ >= 110
|
||||
if(spotFallOff <= 0.0){
|
||||
gl_FragColor.rgb = AmbientSum * diffuseColor.rgb;
|
||||
gl_FragColor.a = alpha;
|
||||
return;
|
||||
}else{
|
||||
spotFallOff = clamp(spotFallOff, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
#else
|
||||
spotFallOff = clamp(spotFallOff, step(g_LightDirection.w, 0.001), 1.0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// ***********************
|
||||
// Read from textures
|
||||
// ***********************
|
||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||
vec3 normal = newNormal;
|
||||
#elif !defined(VERTEX_LIGHTING)
|
||||
vec3 normal = vNormal;
|
||||
#if !defined(LOW_QUALITY) && !defined(V_TANGENT)
|
||||
normal = normalize(normal);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef SPECULARMAP
|
||||
vec4 specularColor = texture2D(m_SpecularMap, newTexCoord);
|
||||
#else
|
||||
vec4 specularColor = vec4(1.0);
|
||||
#endif
|
||||
|
||||
#ifdef LIGHTMAP
|
||||
vec3 lightMapColor;
|
||||
#ifdef SEPARATE_TEXCOORD
|
||||
lightMapColor = texture2D(m_LightMap, texCoord2).rgb;
|
||||
#else
|
||||
lightMapColor = texture2D(m_LightMap, texCoord).rgb;
|
||||
#endif
|
||||
specularColor.rgb *= lightMapColor;
|
||||
diffuseColor.rgb *= lightMapColor;
|
||||
#endif
|
||||
|
||||
#ifdef VERTEX_LIGHTING
|
||||
vec2 light = vertexLightValues.xy;
|
||||
#ifdef COLORRAMP
|
||||
light.x = texture2D(m_ColorRamp, vec2(light.x, 0.0)).r;
|
||||
light.y = texture2D(m_ColorRamp, vec2(light.y, 0.0)).r;
|
||||
#endif
|
||||
|
||||
#ifndef USE_SCATTERING
|
||||
gl_FragColor.rgb = AmbientSum * diffuseColor.rgb +
|
||||
DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) +
|
||||
SpecularSum * specularColor.rgb * vec3(light.y);
|
||||
#else
|
||||
vec3 color = AmbientSum * diffuseColor.rgb +
|
||||
DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) +
|
||||
SpecularSum * specularColor.rgb * vec3(light.y);
|
||||
gl_FragColor.rgb = calculateGroundColor(vec4(color, 1.0)).rgb;
|
||||
#endif
|
||||
#else
|
||||
vec4 lightDir = vLightDir;
|
||||
lightDir.xyz = normalize(lightDir.xyz);
|
||||
vec3 viewDir = normalize(vViewDir);
|
||||
|
||||
vec2 light = computeLighting(normal, viewDir, lightDir.xyz) * spotFallOff;
|
||||
#ifdef COLORRAMP
|
||||
diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb;
|
||||
specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb;
|
||||
#endif
|
||||
|
||||
// Workaround, since it is not possible to modify varying variables
|
||||
vec4 SpecularSum2 = vec4(SpecularSum, 1.0);
|
||||
#ifdef USE_REFLECTION
|
||||
vec4 refColor = Optics_GetEnvColor(m_EnvMap, refVec.xyz);
|
||||
|
||||
// Interpolate light specularity toward reflection color
|
||||
// Multiply result by specular map
|
||||
specularColor = mix(SpecularSum2 * light.y, refColor, refVec.w) * specularColor;
|
||||
|
||||
SpecularSum2 = vec4(1.0);
|
||||
light.y = 1.0;
|
||||
#endif
|
||||
|
||||
#ifndef USE_SCATTERING
|
||||
gl_FragColor.rgb = AmbientSum * diffuseColor.rgb +
|
||||
DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) +
|
||||
SpecularSum * specularColor.rgb * vec3(light.y);
|
||||
#else
|
||||
vec3 color = AmbientSum * diffuseColor.rgb +
|
||||
DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) +
|
||||
SpecularSum * specularColor.rgb * vec3(light.y);
|
||||
gl_FragColor.rgb = calculateGroundColor(vec4(color, 1.0)).rgb;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
gl_FragColor.a = alpha;
|
||||
}
|
||||
352
simarboreal/assets/MatDefs/MultiResolution.j3md
Normal file
@@ -0,0 +1,352 @@
|
||||
MaterialDef Phong Lighting {
|
||||
|
||||
MaterialParameters {
|
||||
|
||||
// Compute vertex lighting in the shader
|
||||
// For better performance
|
||||
Boolean VertexLighting
|
||||
|
||||
// Use more efficent algorithms to improve performance
|
||||
Boolean LowQuality
|
||||
|
||||
// Improve quality at the cost of performance
|
||||
Boolean HighQuality
|
||||
|
||||
// Output alpha from the diffuse map
|
||||
Boolean UseAlpha
|
||||
|
||||
// Alpha threshold for fragment discarding
|
||||
Float AlphaDiscardThreshold (AlphaTestFallOff)
|
||||
|
||||
// Normal map is in BC5/ATI2n/LATC/3Dc compression format
|
||||
Boolean LATC
|
||||
|
||||
// Use the provided ambient, diffuse, and specular colors
|
||||
Boolean UseMaterialColors
|
||||
|
||||
// Activate shading along the tangent, instead of the normal
|
||||
// Requires tangent data to be available on the model.
|
||||
Boolean VTangent
|
||||
|
||||
// Use minnaert diffuse instead of lambert
|
||||
Boolean Minnaert
|
||||
|
||||
// Use ward specular instead of phong
|
||||
Boolean WardIso
|
||||
|
||||
// Use vertex color as an additional diffuse color.
|
||||
Boolean UseVertexColor
|
||||
|
||||
// Ambient color
|
||||
Color Ambient (MaterialAmbient)
|
||||
|
||||
// Diffuse color
|
||||
Color Diffuse (MaterialDiffuse)
|
||||
|
||||
// Specular color
|
||||
Color Specular (MaterialSpecular)
|
||||
|
||||
// Specular power/shininess
|
||||
Float Shininess (MaterialShininess) : 1
|
||||
|
||||
// Diffuse map
|
||||
Texture2D DiffuseMap
|
||||
|
||||
// Diffuse map
|
||||
Texture2D BackgroundDiffuseMap
|
||||
|
||||
// Diffuse map
|
||||
Texture2D NoiseMap
|
||||
|
||||
// Normal map
|
||||
Texture2D NormalMap
|
||||
|
||||
// Specular/gloss map
|
||||
Texture2D SpecularMap
|
||||
|
||||
// Parallax/height map
|
||||
Texture2D ParallaxMap
|
||||
|
||||
//Set to true is parallax map is stored in the alpha channel of the normal map
|
||||
Boolean PackedNormalParallax
|
||||
|
||||
//Sets the relief height for parallax mapping
|
||||
Float ParallaxHeight : 0.05
|
||||
|
||||
//Set to true to activate Steep Parallax mapping
|
||||
Boolean SteepParallax
|
||||
|
||||
// Texture that specifies alpha values
|
||||
Texture2D AlphaMap
|
||||
|
||||
// Color ramp, will map diffuse and specular values through it.
|
||||
Texture2D ColorRamp
|
||||
|
||||
// Texture of the glowing parts of the material
|
||||
Texture2D GlowMap
|
||||
|
||||
// Set to Use Lightmap
|
||||
Texture2D LightMap
|
||||
|
||||
// Set to use TexCoord2 for the lightmap sampling
|
||||
Boolean SeparateTexCoord
|
||||
|
||||
// The glow color of the object
|
||||
Color GlowColor
|
||||
|
||||
// Parameters for fresnel
|
||||
// X = bias
|
||||
// Y = scale
|
||||
// Z = power
|
||||
Vector3 FresnelParams
|
||||
|
||||
// Env Map for reflection
|
||||
TextureCubeMap EnvMap
|
||||
|
||||
// the env map is a spheremap and not a cube map
|
||||
Boolean EnvMapAsSphereMap
|
||||
|
||||
//shadows
|
||||
Int FilterMode
|
||||
Boolean HardwareShadows
|
||||
|
||||
Texture2D ShadowMap0
|
||||
Texture2D ShadowMap1
|
||||
Texture2D ShadowMap2
|
||||
Texture2D ShadowMap3
|
||||
//pointLights
|
||||
Texture2D ShadowMap4
|
||||
Texture2D ShadowMap5
|
||||
|
||||
Float ShadowIntensity
|
||||
Vector4 Splits
|
||||
Vector2 FadeInfo
|
||||
|
||||
Matrix4 LightViewProjectionMatrix0
|
||||
Matrix4 LightViewProjectionMatrix1
|
||||
Matrix4 LightViewProjectionMatrix2
|
||||
Matrix4 LightViewProjectionMatrix3
|
||||
//pointLight
|
||||
Matrix4 LightViewProjectionMatrix4
|
||||
Matrix4 LightViewProjectionMatrix5
|
||||
Vector3 LightPos
|
||||
|
||||
Float PCFEdge
|
||||
Float ShadowMapSize
|
||||
|
||||
// For hardware skinning
|
||||
Int NumberOfBones
|
||||
Matrix4Array BoneMatrices
|
||||
|
||||
|
||||
// Ground scattering parameters
|
||||
Boolean UseScattering
|
||||
Vector3 SunPosition
|
||||
Float Exposure
|
||||
Float KmESun
|
||||
Float InnerRadius
|
||||
Float RadiusScale
|
||||
Float PlanetScale : 1
|
||||
Vector3 InvWavelengthsKrESun
|
||||
Float AverageDensityScale
|
||||
Float InvAverageDensityHeight;
|
||||
Vector3 KWavelengths4PI;
|
||||
|
||||
}
|
||||
|
||||
Technique {
|
||||
|
||||
LightMode MultiPass
|
||||
|
||||
VertexShader GLSL110: MatDefs/MultiResolution.vert
|
||||
FragmentShader GLSL110: MatDefs/MultiResolution.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
NormalMatrix
|
||||
WorldViewMatrix
|
||||
ViewMatrix
|
||||
CameraPosition
|
||||
WorldMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
LATC : LATC
|
||||
VERTEX_COLOR : UseVertexColor
|
||||
VERTEX_LIGHTING : VertexLighting
|
||||
ATTENUATION : Attenuation
|
||||
MATERIAL_COLORS : UseMaterialColors
|
||||
V_TANGENT : VTangent
|
||||
MINNAERT : Minnaert
|
||||
WARDISO : WardIso
|
||||
LOW_QUALITY : LowQuality
|
||||
HQ_ATTENUATION : HighQuality
|
||||
|
||||
DIFFUSEMAP : DiffuseMap
|
||||
NORMALMAP : NormalMap
|
||||
SPECULARMAP : SpecularMap
|
||||
PARALLAXMAP : ParallaxMap
|
||||
NORMALMAP_PARALLAX : PackedNormalParallax
|
||||
STEEP_PARALLAX : SteepParallax
|
||||
ALPHAMAP : AlphaMap
|
||||
COLORRAMP : ColorRamp
|
||||
LIGHTMAP : LightMap
|
||||
SEPARATE_TEXCOORD : SeparateTexCoord
|
||||
|
||||
USE_REFLECTION : EnvMap
|
||||
SPHERE_MAP : SphereMap
|
||||
|
||||
NUM_BONES : NumberOfBones
|
||||
|
||||
USE_SCATTERING : UseScattering
|
||||
}
|
||||
}
|
||||
|
||||
Technique PreShadow {
|
||||
|
||||
VertexShader GLSL110 : Common/MatDefs/Shadow/PreShadow.vert
|
||||
FragmentShader GLSL110 : Common/MatDefs/Shadow/PreShadow.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
WorldViewMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
COLOR_MAP : ColorMap
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
NUM_BONES : NumberOfBones
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
FaceCull Off
|
||||
DepthTest On
|
||||
DepthWrite On
|
||||
PolyOffset 5 3
|
||||
ColorWrite Off
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Technique PostShadow15{
|
||||
VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert
|
||||
FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
WorldMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
HARDWARE_SHADOWS : HardwareShadows
|
||||
FILTER_MODE : FilterMode
|
||||
PCFEDGE : PCFEdge
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
COLOR_MAP : ColorMap
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
NUM_BONES : NumberOfBones
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
Blend Modulate
|
||||
DepthWrite Off
|
||||
PolyOffset -0.1 0
|
||||
}
|
||||
}
|
||||
|
||||
Technique PostShadow{
|
||||
VertexShader GLSL110: Common/MatDefs/Shadow/PostShadow.vert
|
||||
FragmentShader GLSL110: Common/MatDefs/Shadow/PostShadow.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
WorldMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
HARDWARE_SHADOWS : HardwareShadows
|
||||
FILTER_MODE : FilterMode
|
||||
PCFEDGE : PCFEdge
|
||||
DISCARD_ALPHA : AlphaDiscardThreshold
|
||||
COLOR_MAP : ColorMap
|
||||
SHADOWMAP_SIZE : ShadowMapSize
|
||||
FADE : FadeInfo
|
||||
PSSM : Splits
|
||||
POINTLIGHT : LightViewProjectionMatrix5
|
||||
NUM_BONES : NumberOfBones
|
||||
}
|
||||
|
||||
ForcedRenderState {
|
||||
Blend Modulate
|
||||
DepthWrite Off
|
||||
PolyOffset -0.1 0
|
||||
}
|
||||
}
|
||||
|
||||
Technique PreNormalPass {
|
||||
|
||||
VertexShader GLSL110 : Common/MatDefs/SSAO/normal.vert
|
||||
FragmentShader GLSL110 : Common/MatDefs/SSAO/normal.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
WorldViewMatrix
|
||||
NormalMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
DIFFUSEMAP_ALPHA : DiffuseMap
|
||||
NUM_BONES : NumberOfBones
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Technique GBuf {
|
||||
|
||||
VertexShader GLSL110: Common/MatDefs/Light/GBuf.vert
|
||||
FragmentShader GLSL110: Common/MatDefs/Light/GBuf.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
NormalMatrix
|
||||
WorldViewMatrix
|
||||
WorldMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
VERTEX_COLOR : UseVertexColor
|
||||
MATERIAL_COLORS : UseMaterialColors
|
||||
V_TANGENT : VTangent
|
||||
MINNAERT : Minnaert
|
||||
WARDISO : WardIso
|
||||
|
||||
DIFFUSEMAP : DiffuseMap
|
||||
NORMALMAP : NormalMap
|
||||
SPECULARMAP : SpecularMap
|
||||
PARALLAXMAP : ParallaxMap
|
||||
}
|
||||
}
|
||||
|
||||
Technique Glow {
|
||||
|
||||
VertexShader GLSL110: Common/MatDefs/Misc/Unshaded.vert
|
||||
FragmentShader GLSL110: Common/MatDefs/Light/Glow.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
NEED_TEXCOORD1
|
||||
HAS_GLOWMAP : GlowMap
|
||||
HAS_GLOWCOLOR : GlowColor
|
||||
|
||||
NUM_BONES : NumberOfBones
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
237
simarboreal/assets/MatDefs/MultiResolution.vert
Normal file
@@ -0,0 +1,237 @@
|
||||
#define ATTENUATION
|
||||
//#define HQ_ATTENUATION
|
||||
|
||||
#import "Common/ShaderLib/Skinning.glsllib"
|
||||
#import "MatDefs/VertScattering.glsllib"
|
||||
|
||||
|
||||
uniform mat4 g_WorldViewProjectionMatrix;
|
||||
uniform mat4 g_WorldViewMatrix;
|
||||
uniform mat4 g_WorldMatrix;
|
||||
uniform mat3 g_NormalMatrix;
|
||||
uniform mat4 g_ViewMatrix;
|
||||
uniform vec3 g_CameraPosition;
|
||||
|
||||
uniform vec4 m_Ambient;
|
||||
uniform vec4 m_Diffuse;
|
||||
uniform vec4 m_Specular;
|
||||
uniform float m_Shininess;
|
||||
|
||||
uniform vec4 g_LightColor;
|
||||
uniform vec4 g_LightPosition;
|
||||
uniform vec4 g_AmbientLightColor;
|
||||
|
||||
varying vec2 texCoord;
|
||||
#ifdef SEPARATE_TEXCOORD
|
||||
varying vec2 texCoord2;
|
||||
attribute vec2 inTexCoord2;
|
||||
#endif
|
||||
|
||||
varying vec3 AmbientSum;
|
||||
varying vec4 DiffuseSum;
|
||||
varying vec3 SpecularSum;
|
||||
|
||||
varying float z;
|
||||
|
||||
attribute vec3 inPosition;
|
||||
attribute vec2 inTexCoord;
|
||||
attribute vec3 inNormal;
|
||||
|
||||
varying vec3 lightVec;
|
||||
//varying vec4 spotVec;
|
||||
|
||||
#ifdef VERTEX_COLOR
|
||||
attribute vec4 inColor;
|
||||
#endif
|
||||
|
||||
#ifndef VERTEX_LIGHTING
|
||||
attribute vec4 inTangent;
|
||||
|
||||
#ifndef NORMALMAP
|
||||
varying vec3 vNormal;
|
||||
#endif
|
||||
//varying vec3 vPosition;
|
||||
varying vec3 vViewDir;
|
||||
varying vec4 vLightDir;
|
||||
#else
|
||||
varying vec2 vertexLightValues;
|
||||
uniform vec4 g_LightDirection;
|
||||
#endif
|
||||
|
||||
#ifdef USE_REFLECTION
|
||||
uniform vec3 g_CameraPosition;
|
||||
uniform mat4 g_WorldMatrix;
|
||||
|
||||
uniform vec3 m_FresnelParams;
|
||||
varying vec4 refVec;
|
||||
|
||||
|
||||
/**
|
||||
* Input:
|
||||
* attribute inPosition
|
||||
* attribute inNormal
|
||||
* uniform g_WorldMatrix
|
||||
* uniform g_CameraPosition
|
||||
*
|
||||
* Output:
|
||||
* varying refVec
|
||||
*/
|
||||
void computeRef(in vec4 modelSpacePos){
|
||||
vec3 worldPos = (g_WorldMatrix * modelSpacePos).xyz;
|
||||
|
||||
vec3 I = normalize( g_CameraPosition - worldPos ).xyz;
|
||||
vec3 N = normalize( (g_WorldMatrix * vec4(inNormal, 0.0)).xyz );
|
||||
|
||||
refVec.xyz = reflect(I, N);
|
||||
refVec.w = m_FresnelParams.x + m_FresnelParams.y * pow(1.0 + dot(I, N), m_FresnelParams.z);
|
||||
}
|
||||
#endif
|
||||
|
||||
// JME3 lights in world space
|
||||
void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){
|
||||
float posLight = step(0.5, color.w);
|
||||
vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight);
|
||||
lightVec = tempVec;
|
||||
#ifdef ATTENUATION
|
||||
float dist = length(tempVec);
|
||||
lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0);
|
||||
lightDir.xyz = tempVec / vec3(dist);
|
||||
#else
|
||||
lightDir = vec4(normalize(tempVec), 1.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef VERTEX_LIGHTING
|
||||
float lightComputeDiffuse(in vec3 norm, in vec3 lightdir){
|
||||
return max(0.0, dot(norm, lightdir));
|
||||
}
|
||||
|
||||
float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){
|
||||
if (shiny <= 1.0){
|
||||
return 0.0;
|
||||
}
|
||||
#ifndef LOW_QUALITY
|
||||
vec3 H = (viewdir + lightdir) * vec3(0.5);
|
||||
return pow(max(dot(H, norm), 0.0), shiny);
|
||||
#else
|
||||
return 0.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){
|
||||
vec4 lightDir;
|
||||
lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir);
|
||||
float spotFallOff = 1.0;
|
||||
if(g_LightDirection.w != 0.0){
|
||||
vec3 L=normalize(lightVec.xyz);
|
||||
vec3 spotdir = normalize(g_LightDirection.xyz);
|
||||
float curAngleCos = dot(-L, spotdir);
|
||||
float innerAngleCos = floor(g_LightDirection.w) * 0.001;
|
||||
float outerAngleCos = fract(g_LightDirection.w);
|
||||
float innerMinusOuter = innerAngleCos - outerAngleCos;
|
||||
spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0);
|
||||
}
|
||||
float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz);
|
||||
float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess);
|
||||
//specularFactor *= step(0.01, diffuseFactor);
|
||||
return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w)*spotFallOff;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main(){
|
||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||
vec3 modelSpaceNorm = inNormal;
|
||||
|
||||
#ifndef VERTEX_LIGHTING
|
||||
vec3 modelSpaceTan = inTangent.xyz;
|
||||
#endif
|
||||
|
||||
#ifdef NUM_BONES
|
||||
#ifndef VERTEX_LIGHTING
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan);
|
||||
#else
|
||||
Skinning_Compute(modelSpacePos, modelSpaceNorm);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_SCATTERING
|
||||
vec4 wPos = g_WorldMatrix * modelSpacePos;
|
||||
calculateVertexGroundScattering(wPos.xyz, g_CameraPosition);
|
||||
#endif
|
||||
|
||||
gl_Position = g_WorldViewProjectionMatrix * modelSpacePos;
|
||||
texCoord = inTexCoord;
|
||||
#ifdef SEPARATE_TEXCOORD
|
||||
texCoord2 = inTexCoord2;
|
||||
#endif
|
||||
|
||||
vec3 wvPosition = (g_WorldViewMatrix * modelSpacePos).xyz;
|
||||
|
||||
z = length(wvPosition);
|
||||
|
||||
vec3 wvNormal = normalize(g_NormalMatrix * modelSpaceNorm);
|
||||
vec3 viewDir = normalize(-wvPosition);
|
||||
|
||||
//vec4 lightColor = g_LightColor[gl_InstanceID];
|
||||
//vec4 lightPos = g_LightPosition[gl_InstanceID];
|
||||
//vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w));
|
||||
//wvLightPos.w = lightPos.w;
|
||||
|
||||
vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0)));
|
||||
wvLightPos.w = g_LightPosition.w;
|
||||
vec4 lightColor = g_LightColor;
|
||||
|
||||
#if defined(NORMALMAP) && !defined(VERTEX_LIGHTING)
|
||||
vec3 wvTangent = normalize(g_NormalMatrix * modelSpaceTan);
|
||||
vec3 wvBinormal = cross(wvNormal, wvTangent);
|
||||
|
||||
mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal);
|
||||
|
||||
//vPosition = wvPosition * tbnMat;
|
||||
//vViewDir = viewDir * tbnMat;
|
||||
vViewDir = -wvPosition * tbnMat;
|
||||
lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
|
||||
vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz;
|
||||
#elif !defined(VERTEX_LIGHTING)
|
||||
vNormal = wvNormal;
|
||||
|
||||
//vPosition = wvPosition;
|
||||
vViewDir = viewDir;
|
||||
|
||||
lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir);
|
||||
|
||||
#ifdef V_TANGENT
|
||||
vNormal = normalize(g_NormalMatrix * inTangent.xyz);
|
||||
vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//computing spot direction in view space and unpacking spotlight cos
|
||||
// spotVec = (g_ViewMatrix * vec4(g_LightDirection.xyz, 0.0) );
|
||||
// spotVec.w = floor(g_LightDirection.w) * 0.001;
|
||||
// lightVec.w = fract(g_LightDirection.w);
|
||||
|
||||
lightColor.w = 1.0;
|
||||
#ifdef MATERIAL_COLORS
|
||||
AmbientSum = (m_Ambient * g_AmbientLightColor).rgb;
|
||||
DiffuseSum = m_Diffuse * lightColor;
|
||||
SpecularSum = (m_Specular * lightColor).rgb;
|
||||
#else
|
||||
AmbientSum = vec3(0.2, 0.2, 0.2) * g_AmbientLightColor.rgb; // Default: ambient color is dark gray
|
||||
DiffuseSum = lightColor;
|
||||
SpecularSum = vec3(0.0);
|
||||
#endif
|
||||
|
||||
#ifdef VERTEX_COLOR
|
||||
AmbientSum *= inColor.rgb;
|
||||
DiffuseSum *= inColor;
|
||||
#endif
|
||||
|
||||
#ifdef VERTEX_LIGHTING
|
||||
vertexLightValues = computeLighting(wvPosition, wvNormal, viewDir, wvLightPos);
|
||||
#endif
|
||||
|
||||
#ifdef USE_REFLECTION
|
||||
computeRef(modelSpacePos);
|
||||
#endif
|
||||
}
|
||||
10
simarboreal/assets/MatDefs/Null.frag
Normal file
@@ -0,0 +1,10 @@
|
||||
#import "Common/ShaderLib/MultiSample.glsllib"
|
||||
|
||||
uniform COLORTEXTURE m_Texture;
|
||||
varying vec2 texCoord;
|
||||
|
||||
void main() {
|
||||
vec4 texVal = getColor(m_Texture, texCoord);
|
||||
gl_FragColor = texVal;
|
||||
}
|
||||
|
||||
24
simarboreal/assets/MatDefs/Null.j3md
Normal file
@@ -0,0 +1,24 @@
|
||||
MaterialDef Depth Blur {
|
||||
|
||||
MaterialParameters {
|
||||
Int NumSamples
|
||||
Int NumSamplesDepth
|
||||
Texture2D Texture
|
||||
Texture2D DepthTexture
|
||||
}
|
||||
|
||||
Technique {
|
||||
VertexShader GLSL100: Common/MatDefs/Post/Post.vert
|
||||
FragmentShader GLSL100: MatDefs/Null.frag
|
||||
|
||||
WorldParameters {
|
||||
WorldViewProjectionMatrix
|
||||
}
|
||||
|
||||
Defines {
|
||||
RESOLVE_MS : NumSamples
|
||||
RESOLVE_DEPTH_MS : NumSamplesDepth
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
72
simarboreal/assets/MatDefs/Shadows.frag
Normal file
@@ -0,0 +1,72 @@
|
||||
#import "Common/ShaderLib/MultiSample.glsllib"
|
||||
|
||||
//#define SHOW_BOX
|
||||
//#define SHOW_DELTA
|
||||
|
||||
uniform vec2 g_FrustumNearFar;
|
||||
uniform vec4 g_ViewPort;
|
||||
|
||||
uniform vec4 m_ShadowColor;
|
||||
uniform COLORTEXTURE m_FrameTexture;
|
||||
uniform DEPTHTEXTURE m_DepthTexture;
|
||||
|
||||
varying vec3 texCoord;
|
||||
varying vec3 vViewDir;
|
||||
varying vec3 boxScale;
|
||||
|
||||
void main(){
|
||||
vec4 color = vec4(1.0);
|
||||
|
||||
vec2 uv = vec2(gl_FragCoord.x/g_ViewPort.z, gl_FragCoord.y/g_ViewPort.w);
|
||||
|
||||
float zBuffer = getDepth( m_DepthTexture, uv ).r;
|
||||
|
||||
//
|
||||
// z_buffer_value = a + b / z;
|
||||
//
|
||||
// Where:
|
||||
// a = zFar / ( zFar - zNear )
|
||||
// b = zFar * zNear / ( zNear - zFar )
|
||||
// z = distance from the eye to the object
|
||||
//
|
||||
// Which means:
|
||||
// zb - a = b / z;
|
||||
// z * (zb - a) = b
|
||||
// z = b / (zb - a)
|
||||
//
|
||||
float a = g_FrustumNearFar.y / (g_FrustumNearFar.y - g_FrustumNearFar.x);
|
||||
float b = g_FrustumNearFar.y * g_FrustumNearFar.x / (g_FrustumNearFar.x - g_FrustumNearFar.y);
|
||||
float z = b / (zBuffer - a);
|
||||
|
||||
float us = b / (gl_FragCoord.z - a);
|
||||
|
||||
float modelScale = 1.0;
|
||||
|
||||
float delta = (z-us) * modelScale;
|
||||
|
||||
#if defined(SHOW_DELTA)
|
||||
color = vec4(delta, 0.0, 0.0, 1.0);
|
||||
#elif defined(SHOW_BOX)
|
||||
color = vec4(texCoord * boxScale,1.0);
|
||||
#else
|
||||
|
||||
vec3 view = normalize(vViewDir);
|
||||
vec3 scene = texCoord + view * delta;
|
||||
vec3 stu = scene * boxScale;
|
||||
|
||||
float xTex = (0.5 - stu.x) * 2.0;
|
||||
float zTex = (0.5 - stu.z) * 2.0;
|
||||
float t = stu.y;
|
||||
|
||||
float low = (t - 0.75) * 1.33333;
|
||||
float hi = (t - 0.75) * 4.0;
|
||||
float yTex = low * step(t, 0.75) + hi * step(0.75, t);
|
||||
|
||||
float col = sqrt((xTex * xTex) + (zTex * zTex) + (yTex * yTex));
|
||||
float shadow = (1.0 - col);
|
||||
color = vec4(m_ShadowColor);
|
||||
color.a *= clamp(shadow, 0.0, 0.8);
|
||||
#endif
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
29
simarboreal/assets/MatDefs/Shadows.j3md
Normal file
@@ -0,0 +1,29 @@
|
||||
MaterialDef Simple Shadows {
|
||||
|
||||
MaterialParameters {
|
||||
Int NumSamples
|
||||
Int NumSamplesDepth
|
||||
|
||||
Color ShadowColor
|
||||
|
||||
Texture2D FrameTexture
|
||||
Texture2D DepthTexture
|
||||
}
|
||||
|
||||
Technique {
|
||||
VertexShader GLSL120: MatDefs/Shadows.vert
|
||||
FragmentShader GLSL130: MatDefs/Shadows.frag
|
||||
|
||||
WorldParameters {
|
||||
ViewProjectionMatrix
|
||||
FrustumNearFar
|
||||
ViewPort
|
||||
}
|
||||
|
||||
Defines {
|
||||
RESOLVE_MS : NumSamples
|
||||
RESOLVE_DEPTH_MS : NumSamplesDepth
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
22
simarboreal/assets/MatDefs/Shadows.vert
Normal file
@@ -0,0 +1,22 @@
|
||||
|
||||
uniform mat4 g_ViewProjectionMatrix;
|
||||
|
||||
attribute vec3 inPosition; // the world position
|
||||
attribute vec3 inTexCoord; // the model space position, relative to a corner
|
||||
attribute vec3 inTexCoord2; // the x,y,z scale to get from model space to 0->1 space
|
||||
attribute vec3 inNormal; // the view direction in model-space
|
||||
|
||||
varying vec3 texCoord;
|
||||
varying vec3 vViewDir;
|
||||
varying vec3 boxScale;
|
||||
|
||||
|
||||
|
||||
void main(){
|
||||
vec4 modelSpacePos = vec4(inPosition, 1.0);
|
||||
gl_Position = g_ViewProjectionMatrix * modelSpacePos;
|
||||
|
||||
vViewDir = inNormal;
|
||||
texCoord = inTexCoord;
|
||||
boxScale = inTexCoord2;
|
||||
}
|
||||
BIN
simarboreal/assets/Models/female-parts.j3o
Normal file
BIN
simarboreal/assets/Models/male-parts-no-bones.j3o
Normal file
BIN
simarboreal/assets/Textures/brown-dirt-norm.jpg
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
simarboreal/assets/Textures/grass-flat.jpg
Normal file
|
After Width: | Height: | Size: 233 KiB |
BIN
simarboreal/assets/Textures/grass.jpg
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
simarboreal/assets/Textures/test-pattern.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
31
simarboreal/build.gradle
Normal file
@@ -0,0 +1,31 @@
|
||||
plugins {
|
||||
id 'application'
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = 'com.simsilica.arboreal.TreeEditor'
|
||||
applicationDefaultJvmArgs = ['-Xmx512m', '-XX:MaxDirectMemorySize=512m']
|
||||
}
|
||||
|
||||
sourceSets.main.resources {
|
||||
srcDirs += 'src/main/java'
|
||||
exclude '**/*.java'
|
||||
exclude '**/*.tmp'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
runtimeOnly files('assets')
|
||||
}
|
||||
|
||||
tasks.register('extractNatives', Copy) {
|
||||
from zipTree(file('libs/jME3-lwjgl-natives.jar'))
|
||||
into "${buildDir}/natives"
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
}
|
||||
|
||||
run {
|
||||
dependsOn extractNatives
|
||||
workingDir = rootDir
|
||||
jvmArgs "-Djava.library.path=${buildDir}/natives"
|
||||
}
|
||||
BIN
simarboreal/libs/Lemur.jar
Normal file
BIN
simarboreal/libs/LemurProps.jar
Normal file
BIN
simarboreal/libs/Pager.jar
Normal file
BIN
simarboreal/libs/SimArboreal.jar
Normal file
BIN
simarboreal/libs/arboreal-assets.jar
Normal file
BIN
simarboreal/libs/assets.jar
Normal file
BIN
simarboreal/libs/groovy-all-2.1.9.jar
Normal file
BIN
simarboreal/libs/guava-12.0.jar
Normal file
BIN
simarboreal/libs/jME3-core.jar
Normal file
BIN
simarboreal/libs/jME3-desktop.jar
Normal file
BIN
simarboreal/libs/jME3-effects.jar
Normal file
BIN
simarboreal/libs/jME3-lwjgl-natives.jar
Normal file
BIN
simarboreal/libs/jME3-lwjgl.jar
Normal file
BIN
simarboreal/libs/jME3-plugins.jar
Normal file
BIN
simarboreal/libs/jinput.jar
Normal file
BIN
simarboreal/libs/log4j-1.2.12.jar
Normal file
BIN
simarboreal/libs/lwjgl.jar
Normal file
BIN
simarboreal/libs/meta-jb-json-1.0.1.jar
Normal file
BIN
simarboreal/libs/slf4j-api-1.7.5.jar
Normal file
BIN
simarboreal/libs/slf4j-log4j12-1.7.5.jar
Normal file
15
simarboreal/nbproject/assets-impl.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--assets-impl.xml v1.0-->
|
||||
<project name="assets-impl" basedir="..">
|
||||
<target name="-init-assets">
|
||||
<jar jarfile="${build.dir}/${assets.jar.name}" excludes="${assets.excludes}" basedir="${assets.folder.name}" compress="${assets.compress}"/>
|
||||
<property location="${assets.folder.name}" name="assets.dir.resolved"/>
|
||||
<property location="${build.dir}/${assets.jar.name}" name="assets.jar.resolved"/>
|
||||
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
|
||||
<pathconvert property="run.classpath.without.build.classes.dir">
|
||||
<path path="${run.classpath}"/>
|
||||
<map from="${build.classes.dir.resolved}" to=""/>
|
||||
<map from="${assets.dir.resolved}" to="${assets.jar.resolved}"/>
|
||||
</pathconvert>
|
||||
</target>
|
||||
</project>
|
||||
1521
simarboreal/nbproject/build-impl.xml
Normal file
8
simarboreal/nbproject/genfiles.properties
Normal file
@@ -0,0 +1,8 @@
|
||||
build.xml.data.CRC32=94bf7c61
|
||||
build.xml.script.CRC32=79a29eb7
|
||||
build.xml.stylesheet.CRC32=958a1d3e@1.32.1.45
|
||||
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
|
||||
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
|
||||
nbproject/build-impl.xml.data.CRC32=0f2662fc
|
||||
nbproject/build-impl.xml.script.CRC32=bb94fa75
|
||||
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48
|
||||
124
simarboreal/nbproject/project.properties
Normal file
@@ -0,0 +1,124 @@
|
||||
annotation.processing.enabled=true
|
||||
annotation.processing.enabled.in.editor=false
|
||||
annotation.processing.processors.list=
|
||||
annotation.processing.run.all.processors=true
|
||||
ant.customtasks.libs=launch4j
|
||||
application.desc=Editor for the tree parameters necessary for generating trees.
|
||||
application.homepage=https://code.google.com/p/simsilica-tools/
|
||||
application.splash=C:\\Development\\google\\simsilica-tools\\trunk\\SimArboreal-Editor\\TreeEditor-Splash.png
|
||||
application.title=SimArboreal-Editor
|
||||
application.vendor=Simsilica, LLC
|
||||
assets.jar.name=assets.jar
|
||||
assets.excludes=**/*.j3odata,**/*.mesh,**/*.skeleton,**/*.mesh.xml,**/*.skeleton.xml,**/*.scene,**/*.material,**/*.obj,**/*.mtl,**/*.3ds,**/*.dae,**/*.blend,**/*.blend*[0-9],**/.backups/**,**/*.psd
|
||||
assets.folder.name=assets
|
||||
assets.compress=true
|
||||
build.classes.dir=${build.dir}/classes
|
||||
build.classes.excludes=**/*.java,**/*.form,**/.backups/**
|
||||
# This directory is removed when the project is cleaned:
|
||||
build.dir=build
|
||||
build.generated.dir=${build.dir}/generated
|
||||
build.generated.sources.dir=${build.dir}/generated-sources
|
||||
# Only compile against the classpath explicitly listed here:
|
||||
build.sysclasspath=ignore
|
||||
build.test.classes.dir=${build.dir}/test/classes
|
||||
build.test.results.dir=${build.dir}/test/results
|
||||
compile.on.save=true
|
||||
# Uncomment to specify the preferred debugger connection transport:
|
||||
#debug.transport=dt_socket
|
||||
debug.classpath=\
|
||||
${run.classpath}
|
||||
debug.test.classpath=\
|
||||
${run.test.classpath}
|
||||
# This directory is removed when the project is cleaned:
|
||||
dist.dir=dist
|
||||
dist.jar=${dist.dir}/${application.title}.jar
|
||||
dist.javadoc.dir=${dist.dir}/javadoc
|
||||
endorsed.classpath=
|
||||
excludes=
|
||||
file.reference.arboreal-assets.jar=..\\SimArboreal\\dist\\lib\\arboreal-assets.jar
|
||||
file.reference.groovy-all-2.1.9.jar=lib\\groovy-all-2.1.9.jar
|
||||
file.reference.guava-12.0.jar=lib\\guava-12.0.jar
|
||||
file.reference.log4j-1.2.12.jar=lib\\log4j-1.2.12.jar
|
||||
file.reference.meta-jb-json-1.0.1.jar=lib\\meta-jb-json-1.0.1.jar
|
||||
file.reference.simfx-assets.jar=..\\SimFX\\dist\\lib\\simfx-assets.jar
|
||||
file.reference.slf4j-api-1.7.5.jar=lib\\slf4j-api-1.7.5.jar
|
||||
file.reference.slf4j-log4j12-1.7.5.jar=lib\\slf4j-log4j12-1.7.5.jar
|
||||
includes=**
|
||||
jar.compress=false
|
||||
javac.classpath=\
|
||||
${reference.SimArboreal.jar}:\
|
||||
${libs.jme3-lwjgl.classpath}:\
|
||||
${libs.jme3-effects.classpath}:\
|
||||
${libs.jme3-desktop.classpath}:\
|
||||
${file.reference.arboreal-assets.jar}:\
|
||||
${reference.Pager.jar}:\
|
||||
${reference.SimFX.jar}:\
|
||||
${libs.jme3-core.classpath}:\
|
||||
${reference.Lemur.jar}:\
|
||||
${reference.LemurProps.jar}:\
|
||||
${file.reference.groovy-all-2.1.9.jar}:\
|
||||
${file.reference.guava-12.0.jar}:\
|
||||
${file.reference.log4j-1.2.12.jar}:\
|
||||
${file.reference.meta-jb-json-1.0.1.jar}:\
|
||||
${file.reference.slf4j-api-1.7.5.jar}:\
|
||||
${file.reference.slf4j-log4j12-1.7.5.jar}:\
|
||||
${file.reference.simfx-assets.jar}
|
||||
# Space-separated list of extra javac options
|
||||
javac.compilerargs=
|
||||
javac.deprecation=false
|
||||
javac.processorpath=\
|
||||
${javac.classpath}
|
||||
javac.source=1.6
|
||||
javac.target=1.6
|
||||
javac.test.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}
|
||||
javadoc.additionalparam=
|
||||
javadoc.author=false
|
||||
javadoc.encoding=${source.encoding}
|
||||
javadoc.noindex=false
|
||||
javadoc.nonavbar=false
|
||||
javadoc.notree=false
|
||||
javadoc.private=false
|
||||
javadoc.splitindex=true
|
||||
javadoc.use=true
|
||||
javadoc.version=false
|
||||
javadoc.windowtitle=
|
||||
jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
|
||||
jnlp.codebase.type=local
|
||||
jnlp.descriptor=application
|
||||
jnlp.enabled=false
|
||||
jnlp.offline-allowed=false
|
||||
jnlp.signed=false
|
||||
launch4j.exe.enabled=true
|
||||
linux.launcher.enabled=true
|
||||
mac.app.enabled=true
|
||||
main.class=com.simsilica.arboreal.TreeEditor
|
||||
meta.inf.dir=${src.dir}/META-INF
|
||||
manifest.file=MANIFEST.MF
|
||||
mkdist.disabled=false
|
||||
platform.active=JDK_1.7
|
||||
project.Lemur=../../Lemur
|
||||
project.LemurProps=../../Lemur/extensions/LemurProps
|
||||
project.Pager=../Pager
|
||||
project.SimArboreal=../SimArboreal
|
||||
project.SimFX=../SimFX
|
||||
reference.Lemur.jar=${project.Lemur}/dist/Lemur.jar
|
||||
reference.LemurProps.jar=${project.LemurProps}/dist/LemurProps.jar
|
||||
reference.Pager.jar=${project.Pager}/dist/Pager.jar
|
||||
reference.SimArboreal.jar=${project.SimArboreal}/dist/SimArboreal.jar
|
||||
reference.SimFX.jar=${project.SimFX}/dist/SimFX.jar
|
||||
run.classpath=\
|
||||
${javac.classpath}:\
|
||||
${build.classes.dir}:\
|
||||
${assets.folder.name}
|
||||
# Space-separated list of JVM arguments used when running the project
|
||||
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
|
||||
# or test-sys-prop.name=value to set system properties for unit tests):
|
||||
run.jvmargs=-Xmx512m -XX:MaxDirectMemorySize=512m
|
||||
run.test.classpath=\
|
||||
${javac.test.classpath}:\
|
||||
${build.test.classes.dir}
|
||||
source.encoding=UTF-8
|
||||
src.java.dir=src\\main\\java
|
||||
src.resources.dir=src\\main\\resources
|
||||
71
simarboreal/nbproject/project.xml
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://www.netbeans.org/ns/project/1">
|
||||
<type>org.netbeans.modules.java.j2seproject</type>
|
||||
<configuration>
|
||||
<buildExtensions xmlns="http://www.netbeans.org/ns/ant-build-extender/1">
|
||||
<extension file="assets-impl.xml" id="assets">
|
||||
<dependency dependsOn="-init-assets" target="-do-init"/>
|
||||
</extension>
|
||||
<extension file="launch4j-impl.xml" id="launch4j">
|
||||
<dependency dependsOn="-launch4j-exe" target="jar"/>
|
||||
</extension>
|
||||
<extension file="macapp-impl.xml" id="macapp">
|
||||
<dependency dependsOn="-mac-app" target="jar"/>
|
||||
</extension>
|
||||
<extension file="linuxlauncher-impl.xml" id="linuxlauncher">
|
||||
<dependency dependsOn="-linux-launcher" target="jar"/>
|
||||
</extension>
|
||||
</buildExtensions>
|
||||
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
|
||||
<name>SimArboreal-Editor</name>
|
||||
<explicit-platform explicit-source-supported="true"/>
|
||||
<source-roots>
|
||||
<root id="src.resources.dir"/>
|
||||
<root id="src.java.dir"/>
|
||||
</source-roots>
|
||||
<test-roots/>
|
||||
</data>
|
||||
<references xmlns="http://www.netbeans.org/ns/ant-project-references/1">
|
||||
<reference>
|
||||
<foreign-project>Lemur</foreign-project>
|
||||
<artifact-type>jar</artifact-type>
|
||||
<script>build.xml</script>
|
||||
<target>jar</target>
|
||||
<clean-target>clean</clean-target>
|
||||
<id>jar</id>
|
||||
</reference>
|
||||
<reference>
|
||||
<foreign-project>LemurProps</foreign-project>
|
||||
<artifact-type>jar</artifact-type>
|
||||
<script>build.xml</script>
|
||||
<target>jar</target>
|
||||
<clean-target>clean</clean-target>
|
||||
<id>jar</id>
|
||||
</reference>
|
||||
<reference>
|
||||
<foreign-project>Pager</foreign-project>
|
||||
<artifact-type>jar</artifact-type>
|
||||
<script>build.xml</script>
|
||||
<target>jar</target>
|
||||
<clean-target>clean</clean-target>
|
||||
<id>jar</id>
|
||||
</reference>
|
||||
<reference>
|
||||
<foreign-project>SimArboreal</foreign-project>
|
||||
<artifact-type>jar</artifact-type>
|
||||
<script>build.xml</script>
|
||||
<target>jar</target>
|
||||
<clean-target>clean</clean-target>
|
||||
<id>jar</id>
|
||||
</reference>
|
||||
<reference>
|
||||
<foreign-project>SimFX</foreign-project>
|
||||
<artifact-type>jar</artifact-type>
|
||||
<script>build.xml</script>
|
||||
<target>jar</target>
|
||||
<clean-target>clean</clean-target>
|
||||
<id>jar</id>
|
||||
</reference>
|
||||
</references>
|
||||
</configuration>
|
||||
</project>
|
||||
BIN
simarboreal/release/SimArboreal-Editor-Linux.zip
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
java -Xmx512m -XX:MaxDirectMemorySize=512m -jar SimArboreal-Editor.jar
|
||||
|
||||
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/Lemur.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/LemurProps.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/Pager.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/SimArboreal.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/assets.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/guava-12.0.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/jME3-core.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/jME3-lwjgl.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/jinput.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/lib/lwjgl.jar
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/liblwjgl64.so
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Linux/libopenal64.so
Normal file
BIN
simarboreal/release/SimArboreal-Editor-MacOSX.zip
Normal file
BIN
simarboreal/release/SimArboreal-Editor-Windows.zip
Normal file
71
simarboreal/release/SimArboreal-Editor-changelog.txt
Normal file
@@ -0,0 +1,71 @@
|
||||
Revision ???
|
||||
-------------
|
||||
- Added a dependency to the SimFX package and converted to use
|
||||
its LightingState and SkyState (with scattering)
|
||||
- Grass plane supports atmospherics and a toggle was added to
|
||||
the visualization options panel.
|
||||
- Added atmospheric support for trees along with a toggle.
|
||||
|
||||
|
||||
|
||||
Revision 143
|
||||
-------------
|
||||
- Added an action to save the tree atlas images
|
||||
as PNG files.
|
||||
- Fixed how the atlas textures are generated so that they
|
||||
save as embedded textures in the j3o.
|
||||
- Fixed impostor meshes to use short buffers instead of int.
|
||||
- Added toggleable noise-based wind
|
||||
- Added a video recodring option F12
|
||||
- Added editors for the tree wind-related parameters
|
||||
- Changed the tree parameters file extension to just plain
|
||||
.simap The old simap.json extension is still supported for
|
||||
loading and the file format itself hasn't changed.
|
||||
|
||||
|
||||
Revision 126
|
||||
-------------
|
||||
- Reworked the FileActionsState to better allow embedding
|
||||
in external applications. Save and load methods were added
|
||||
and the buttons are now not added to the UI unless the state
|
||||
is enabled. This should help facilitate a parallel JME SDK
|
||||
plug-in effort.
|
||||
|
||||
|
||||
Revision 94
|
||||
------------
|
||||
- Moved RollupPanel and TabbedPanel out into Lemur core.
|
||||
- Moved the builder classes out into the new simsilica-tools Pager
|
||||
library.
|
||||
- Moved PropertyPanel into its own Lemur extension project LemurProps.
|
||||
- Converted to use the now-standard Lemur 'glass' style with just a few
|
||||
local custom extensions.
|
||||
- Moved the Builderstate out to the builder project.
|
||||
|
||||
|
||||
Revision 69
|
||||
-------------
|
||||
- Added Y offset parameter that is separate from trunk
|
||||
height.
|
||||
- Added LOD support including two mesh reduction strategies:
|
||||
Flat-Poly : renders the tree branches as a set of axis-aligned
|
||||
billboarded flat quads.
|
||||
Impostor : renders a single quad with a view-direction indexed texture.
|
||||
(Note: impostors currently don't save properly to the j3o)
|
||||
- Better visual separate of child properies in the UI.
|
||||
- Reorganized UI to include outer rollup panels to separate vis
|
||||
settings from tree parameters.
|
||||
- Added an avatar toggle to the UI.
|
||||
- Added shadow intensity and lighting direction settings to the
|
||||
UI.
|
||||
- Added a simplified 'drop shadow' filter that can be enabled instead
|
||||
of regular shadows.
|
||||
- File write operations now warn before overwriting existing files.
|
||||
- Cleaned out the wire frame meshes from the exported j3o during save.
|
||||
- Renamed the tree geometry elements to make more sense when viewing
|
||||
the tree object in something like Scene Composer.
|
||||
|
||||
|
||||
Revision 33
|
||||
-------------
|
||||
- Initial release
|
||||
BIN
simarboreal/release/SimArboreal-Editor.jar
Normal file
1
simarboreal/settings.gradle.bak
Normal file
@@ -0,0 +1 @@
|
||||
rootProject.name = 'sim-arboreal-editor'
|
||||
@@ -0,0 +1,576 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.simsilica.builder.BuilderState;
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.font.BitmapFont;
|
||||
import com.jme3.font.BitmapText;
|
||||
import com.jme3.light.AmbientLight;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.Renderer;
|
||||
import com.jme3.renderer.ViewPort;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import com.jme3.scene.debug.WireBox;
|
||||
import com.jme3.scene.shape.Quad;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Image.Format;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import com.simsilica.arboreal.mesh.BillboardedLeavesMeshGenerator;
|
||||
import com.simsilica.arboreal.mesh.SkinnedTreeMeshGenerator;
|
||||
import com.simsilica.arboreal.mesh.Vertex;
|
||||
import com.simsilica.builder.Builder;
|
||||
import com.simsilica.builder.BuilderReference;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class AtlasGeneratorState extends BaseAppState {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(AtlasGeneratorState.class);
|
||||
|
||||
private VersionedReference<TreeParameters> treeParametersRef;
|
||||
private Material treeMaterial;
|
||||
private Material leafMaterial;
|
||||
|
||||
private Builder builder;
|
||||
private AtlasTreeBuilderReference builderRef;
|
||||
|
||||
private Mesh trunkMesh;
|
||||
private Mesh leafMesh;
|
||||
|
||||
private FrameBuffer diffuseFb;
|
||||
private FrameBuffer normalFb;
|
||||
private CellView[] cellViews = new CellView[8];
|
||||
private Image diffuseMap;
|
||||
private Texture2D diffuseTexture;
|
||||
private Image normalMap;
|
||||
private Texture2D normalTexture;
|
||||
private int needTextureUpdate;
|
||||
|
||||
private BitmapFont font;
|
||||
|
||||
private boolean debugTextures = false;
|
||||
|
||||
private boolean useNormalMaps = true;
|
||||
|
||||
public AtlasGeneratorState() {
|
||||
}
|
||||
|
||||
public Image getDiffuseMap() {
|
||||
return diffuseMap;
|
||||
}
|
||||
|
||||
public Image getNormalMap() {
|
||||
return normalMap;
|
||||
}
|
||||
|
||||
protected Image createFrameBufferImage( FrameBuffer fb ) {
|
||||
int width = fb.getWidth();
|
||||
int height = fb.getHeight();
|
||||
int size = width * height * 4;
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(size);
|
||||
Image.Format format = fb.getColorBuffer().getFormat();
|
||||
|
||||
// I guess readFrameBuffer always writes in the same
|
||||
// format regardless of the frame buffer format
|
||||
format = Format.BGRA8;
|
||||
return new Image(format, width, height, buffer);
|
||||
}
|
||||
|
||||
protected void updateTextures() {
|
||||
|
||||
Renderer renderer = getApplication().getRenderer();
|
||||
if( diffuseMap == null ) {
|
||||
diffuseMap = createFrameBufferImage(diffuseFb);
|
||||
diffuseTexture = new Texture2D(diffuseMap);
|
||||
getState(ForestGridState.class).getImpostorMaterial().setTexture("DiffuseMap", diffuseTexture);
|
||||
}
|
||||
renderer.readFrameBuffer(diffuseFb, diffuseMap.getData(0));
|
||||
diffuseMap.setUpdateNeeded();
|
||||
|
||||
if( normalMap == null ) {
|
||||
normalMap = createFrameBufferImage(normalFb);
|
||||
normalTexture = new Texture2D(normalMap);
|
||||
if( useNormalMaps ) {
|
||||
getState(ForestGridState.class).getImpostorMaterial().setTexture("NormalMap", normalTexture);
|
||||
}
|
||||
}
|
||||
renderer.readFrameBuffer(normalFb, normalMap.getData(0));
|
||||
normalMap.setUpdateNeeded();
|
||||
|
||||
needTextureUpdate = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
this.treeParametersRef = getState(TreeParametersState.class).getTreeParametersRef();
|
||||
this.treeMaterial = getState(ForestGridState.class).getTreeMaterial();
|
||||
this.leafMaterial = getState(ForestGridState.class).getLeafMaterial();
|
||||
|
||||
this.builder = getState(BuilderState.class).getBuilder();
|
||||
this.builderRef = new AtlasTreeBuilderReference();
|
||||
|
||||
|
||||
this.font = GuiGlobals.getInstance().loadFont("Interface/Fonts/Default.fnt");
|
||||
|
||||
Camera camera = app.getCamera().clone();
|
||||
camera.resize(256, 256, true);
|
||||
camera.resize(1024, 256, false);
|
||||
|
||||
|
||||
FrameBuffer fb1 = new FrameBuffer(1024, 256, 1);
|
||||
diffuseFb = fb1;
|
||||
Texture2D fbTex1 = new Texture2D(1024, 256, Format.RGBA8);
|
||||
fb1.setDepthBuffer(Format.Depth);
|
||||
fb1.setColorTexture(fbTex1);
|
||||
|
||||
FrameBuffer fb2 = new FrameBuffer(1024, 256, 1);
|
||||
normalFb = fb2;
|
||||
Texture2D fbTex2 = new Texture2D(1024, 256, Format.RGBA8);
|
||||
fb2.setDepthBuffer(Format.Depth);
|
||||
fb2.setColorTexture(fbTex2);
|
||||
|
||||
|
||||
if( debugTextures ) {
|
||||
Quad testQuad = new Quad(512, 128);
|
||||
Geometry testGeom = new Geometry("test", testQuad);
|
||||
Material mat = GuiGlobals.getInstance().createMaterial(fbTex1, false).getMaterial();
|
||||
testGeom.setMaterial(mat);
|
||||
((TreeEditor)app).getGuiNode().attachChild(testGeom);
|
||||
|
||||
testQuad = new Quad(512, 128);
|
||||
testGeom = new Geometry("test", testQuad);
|
||||
testGeom.setLocalTranslation(0, 128, 0);
|
||||
mat = GuiGlobals.getInstance().createMaterial(fbTex2, false).getMaterial();
|
||||
testGeom.setMaterial(mat);
|
||||
((TreeEditor)app).getGuiNode().attachChild(testGeom);
|
||||
|
||||
updateTextures();
|
||||
|
||||
testQuad = new Quad(512, 128);
|
||||
testGeom = new Geometry("test", testQuad);
|
||||
testGeom.setLocalTranslation(0, 256, 0);
|
||||
mat = GuiGlobals.getInstance().createMaterial(diffuseTexture, false).getMaterial();
|
||||
testGeom.setMaterial(mat);
|
||||
((TreeEditor)app).getGuiNode().attachChild(testGeom);
|
||||
|
||||
testQuad = new Quad(512, 128);
|
||||
testGeom = new Geometry("test", testQuad);
|
||||
testGeom.setLocalTranslation(0, 384, 0);
|
||||
mat = GuiGlobals.getInstance().createMaterial(normalTexture, false).getMaterial();
|
||||
testGeom.setMaterial(mat);
|
||||
((TreeEditor)app).getGuiNode().attachChild(testGeom);
|
||||
}
|
||||
|
||||
DirectionalLight sun = new DirectionalLight();
|
||||
//sun.setDirection(new Vector3f(0, -1f, -1).normalizeLocal());
|
||||
sun.setDirection(new Vector3f(0, 0, -1).normalizeLocal());
|
||||
|
||||
AmbientLight ambient = new AmbientLight();
|
||||
|
||||
if( useNormalMaps ) {
|
||||
sun.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 1));
|
||||
ambient.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 1));
|
||||
} else {
|
||||
sun.setColor(new ColorRGBA(1, 1, 1, 1));
|
||||
ambient.setColor(new ColorRGBA(0.25f, 0.25f, 0.25f, 1));
|
||||
}
|
||||
|
||||
//-x * FastMath.TWO_PI - FastMath.QUARTER_PI
|
||||
// The texture quads actually run a, c, d, b starting with
|
||||
// the +, + quadrant
|
||||
cellViews[0] = new CellView(fb1, camera, sun, ambient, FastMath.QUARTER_PI, 0.25f * 0);
|
||||
cellViews[1] = new CellView(fb1, camera, sun, ambient, -FastMath.QUARTER_PI, 0.25f * 1);
|
||||
cellViews[2] = new CellView(fb1, camera, sun, ambient, FastMath.PI - FastMath.QUARTER_PI, 0.25f * 2);
|
||||
cellViews[3] = new CellView(fb1, camera, sun, ambient, FastMath.PI + FastMath.QUARTER_PI, 0.25f * 3);
|
||||
|
||||
cellViews[4] = new NormalMapCellView(fb2, camera, sun, ambient, FastMath.QUARTER_PI, 0.25f * 0);
|
||||
cellViews[5] = new NormalMapCellView(fb2, camera, sun, ambient, -FastMath.QUARTER_PI, 0.25f * 1);
|
||||
cellViews[6] = new NormalMapCellView(fb2, camera, sun, ambient, FastMath.PI - FastMath.QUARTER_PI, 0.25f * 2);
|
||||
cellViews[7] = new NormalMapCellView(fb2, camera, sun, ambient, FastMath.PI + FastMath.QUARTER_PI, 0.25f * 3);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
for( CellView view : cellViews ) {
|
||||
app.getRenderManager().removeMainView(view.getViewPort());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
}
|
||||
|
||||
protected void updateTree( Mesh trunkMesh, Mesh leafMesh ) {
|
||||
if( this.trunkMesh == trunkMesh ) {
|
||||
return;
|
||||
}
|
||||
|
||||
releaseMesh(this.trunkMesh);
|
||||
releaseMesh(this.leafMesh);
|
||||
this.trunkMesh = trunkMesh;
|
||||
this.leafMesh = leafMesh;
|
||||
|
||||
for( CellView view : cellViews ) {
|
||||
if( view != null ) {
|
||||
view.updateMesh(trunkMesh, leafMesh);
|
||||
}
|
||||
}
|
||||
|
||||
// Texture updates need to happen one frame late...
|
||||
// but we get the notification that we need the check
|
||||
// early in _this_ frame. ie: updateTree() is called
|
||||
// before our update(), render(). If we want to render
|
||||
// a frame later then we need to skip this frame before
|
||||
// updating textures.
|
||||
needTextureUpdate = 2;
|
||||
}
|
||||
|
||||
protected void releaseMesh( Mesh mesh ) {
|
||||
if( mesh == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the old buffers
|
||||
for( VertexBuffer vb : mesh.getBufferList() ) {
|
||||
if( log.isTraceEnabled() ) {
|
||||
log.trace("--destroying buffer:" + vb);
|
||||
}
|
||||
BufferUtils.destroyDirectBuffer( vb.getData() );
|
||||
}
|
||||
}
|
||||
|
||||
private float nextUpdateCheck = 0.1f;
|
||||
private float lastTpf;
|
||||
@Override
|
||||
public void update( float tpf ) {
|
||||
lastTpf = tpf;
|
||||
|
||||
nextUpdateCheck += tpf;
|
||||
if( nextUpdateCheck <= 0.1f ) {
|
||||
return;
|
||||
}
|
||||
nextUpdateCheck = 0;
|
||||
|
||||
boolean changed = treeParametersRef.update();
|
||||
if( changed ) {
|
||||
builder.build(builderRef);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render( RenderManager rm ) {
|
||||
if( cellViews != null ) {
|
||||
// We update the logical state here because it is
|
||||
// done after the other updates. So if another app
|
||||
// state or control has modified our root then we
|
||||
// are guaranteed to run after.
|
||||
for( CellView view : cellViews ) {
|
||||
if( view != null ) {
|
||||
view.update(lastTpf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Texture updates need to happen one frame later...
|
||||
// but we get the notification that we need the check
|
||||
// early in _this_ frame.
|
||||
if( needTextureUpdate > 0 ) {
|
||||
needTextureUpdate--;
|
||||
if( needTextureUpdate == 0 ) {
|
||||
updateTextures();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CellView {
|
||||
private ViewPort viewport;
|
||||
private Camera camera;
|
||||
private Node root;
|
||||
private Mesh leafMesh;
|
||||
private Mesh trunkMesh;
|
||||
private Geometry trunkGeom;
|
||||
private Geometry leafGeom;
|
||||
private Geometry wireBounds;
|
||||
private boolean debugBounds = false;
|
||||
private boolean debugCell = false;
|
||||
|
||||
public CellView( FrameBuffer fb, Camera templateCamera, DirectionalLight sun, AmbientLight ambient, float angle, float x ) {
|
||||
|
||||
this.camera = templateCamera.clone();
|
||||
camera.setViewPort(x, x + 0.25f, 0, 1);
|
||||
|
||||
this.root = new Node("CellRoot:" + x );
|
||||
this.viewport = getApplication().getRenderManager().createMainView("AtlasCell[" + x + "]", camera);
|
||||
this.viewport.setOutputFrameBuffer(fb);
|
||||
this.root.rotate(0, -angle, 0);
|
||||
|
||||
if( debugCell ) {
|
||||
BitmapText label = new BitmapText(font);
|
||||
label.setText("u:" + x + "\na:" + angle);
|
||||
label.setLocalScale(0.01f);
|
||||
Quaternion labelRot = root.getLocalRotation().inverse();
|
||||
label.setLocalRotation(labelRot);
|
||||
label.setLocalTranslation(labelRot.mult(new Vector3f(0, 1, 2)));
|
||||
root.attachChild(label);
|
||||
}
|
||||
|
||||
viewport.attachScene(root);
|
||||
root.addLight(sun);
|
||||
root.addLight(ambient);
|
||||
|
||||
viewport.setClearFlags(true, true, true);
|
||||
viewport.setBackgroundColor(new ColorRGBA(0, 0, 0, 0));
|
||||
this.camera.lookAtDirection(new Vector3f(0, 0, -1), Vector3f.UNIT_Y);
|
||||
}
|
||||
|
||||
public ViewPort getViewPort() {
|
||||
return viewport;
|
||||
}
|
||||
|
||||
public void update( float tpf ) {
|
||||
root.updateLogicalState(tpf);
|
||||
root.updateGeometricState();
|
||||
}
|
||||
|
||||
protected Material getTreeMaterial() {
|
||||
return treeMaterial;
|
||||
}
|
||||
|
||||
protected Material getLeafMaterial() {
|
||||
return leafMaterial;
|
||||
}
|
||||
|
||||
public void updateMesh( Mesh trunkMesh, Mesh leafMesh ) {
|
||||
if( trunkGeom == null ) {
|
||||
// Create it
|
||||
trunkGeom = new Geometry("Trunk", trunkMesh);
|
||||
trunkGeom.setMaterial(getTreeMaterial());
|
||||
root.attachChild(trunkGeom);
|
||||
} else {
|
||||
// Just swap out the mesh
|
||||
trunkGeom.setMesh(trunkMesh);
|
||||
}
|
||||
this.trunkMesh = trunkMesh;
|
||||
this.leafMesh = leafMesh;
|
||||
if( leafMesh == null ) {
|
||||
if( leafGeom != null ) {
|
||||
leafGeom.removeFromParent();
|
||||
leafGeom = null;
|
||||
}
|
||||
} else {
|
||||
if( leafGeom == null ) {
|
||||
// Create it
|
||||
leafGeom = new Geometry("Leaves", leafMesh);
|
||||
leafGeom.setMaterial(getLeafMaterial());
|
||||
leafGeom.setQueueBucket(Bucket.Transparent);
|
||||
root.attachChild(leafGeom);
|
||||
} else {
|
||||
// Just swap out the mesh
|
||||
leafGeom.setMesh(leafMesh);
|
||||
}
|
||||
}
|
||||
updateCamera();
|
||||
}
|
||||
|
||||
protected void updateCamera() {
|
||||
|
||||
BoundingBox bb = (BoundingBox)trunkMesh.getBound();
|
||||
if( leafGeom != null ) {
|
||||
BoundingBox bb2 = (BoundingBox)leafMesh.getBound();
|
||||
bb = (BoundingBox)bb.merge(bb2);
|
||||
}
|
||||
|
||||
Vector3f min = bb.getMin(null);
|
||||
Vector3f max = bb.getMax(null);
|
||||
|
||||
float xSize = Math.max(Math.abs(min.x), Math.abs(max.x));
|
||||
float ySize = max.y - min.y;
|
||||
float zSize = Math.max(Math.abs(min.z), Math.abs(max.z));
|
||||
|
||||
float size = ySize * 0.5f;
|
||||
size = Math.max(size, xSize);
|
||||
size = Math.max(size, zSize);
|
||||
|
||||
// In the projection matrix, [1][1] should be:
|
||||
// (2 * Zn) / camHeight
|
||||
// where Zn is distance to near plane.
|
||||
float m11 = camera.getViewProjectionMatrix().m11;
|
||||
|
||||
// We want our position to be such that
|
||||
// 'size' is otherwise = cameraHeight when rendered.
|
||||
float z = m11 * size;
|
||||
|
||||
// Add the z extents so that we adjust for the near plane
|
||||
// of the bounding box... well we will be rotating so
|
||||
// let's just be sure and take the max of x and z
|
||||
//float offset = Math.max(bb.getXExtent(), bb.getZExtent());
|
||||
//z += offset;
|
||||
// This creates problems because it makes way too much
|
||||
// space around the tree. A proper solution would require
|
||||
// a bunch of math and in the end would also have to be duplicated
|
||||
// on the quad generation side or somehow stored with the atlas.
|
||||
|
||||
Vector3f center = bb.getCenter();
|
||||
|
||||
float sizeOffset = size - (ySize*0.5f);
|
||||
|
||||
Vector3f camLoc = new Vector3f(0, center.y + sizeOffset, z);
|
||||
camera.setLocation(camLoc);
|
||||
|
||||
if( debugBounds ) {
|
||||
WireBox box;
|
||||
if( wireBounds == null ) {
|
||||
box = new WireBox();
|
||||
wireBounds = new Geometry("wire box", box);
|
||||
Material mat = GuiGlobals.getInstance().createMaterial(ColorRGBA.Yellow, false).getMaterial();
|
||||
wireBounds.setMaterial(mat);
|
||||
root.attachChild(wireBounds);
|
||||
} else {
|
||||
box = (WireBox)wireBounds.getMesh();
|
||||
}
|
||||
box.updatePositions(bb.getXExtent(), bb.getYExtent(), bb.getZExtent());
|
||||
box.setBound(new BoundingBox(new Vector3f(0,0,0), 0, 0, 0));
|
||||
wireBounds.setLocalTranslation(bb.getCenter());
|
||||
wireBounds.setLocalRotation(leafGeom.getLocalRotation());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private class NormalMapCellView extends CellView {
|
||||
public NormalMapCellView( FrameBuffer fb, Camera templateCamera, DirectionalLight sun, AmbientLight ambient, float angle, float x ) {
|
||||
super(fb, templateCamera, sun, ambient, angle, x);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material getTreeMaterial() {
|
||||
Material normalMaterial = treeMaterial.clone();
|
||||
normalMaterial.selectTechnique("PreNormalPass", getApplication().getRenderManager());
|
||||
return normalMaterial;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material getLeafMaterial() {
|
||||
Material normalMaterial = leafMaterial.clone();
|
||||
normalMaterial.selectTechnique("PreNormalPass", getApplication().getRenderManager());
|
||||
return normalMaterial;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class AtlasTreeBuilderReference implements BuilderReference {
|
||||
|
||||
private Mesh trunkMesh;
|
||||
private Mesh leafMesh;
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
// A relatively low priority
|
||||
return 100;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build() {
|
||||
|
||||
TreeParameters treeParameters = treeParametersRef.get();
|
||||
|
||||
TreeGenerator treeGen = new TreeGenerator();
|
||||
Tree tree = treeGen.generateTree(treeParameters);
|
||||
|
||||
SkinnedTreeMeshGenerator meshGen = new SkinnedTreeMeshGenerator();
|
||||
|
||||
List<Vertex> tips = new ArrayList<Vertex>();
|
||||
trunkMesh = meshGen.generateMesh(tree,
|
||||
treeParameters.getLod(0),
|
||||
treeParameters.getYOffset(),
|
||||
treeParameters.getTextureURepeat(),
|
||||
treeParameters.getTextureVScale(),
|
||||
tips);
|
||||
|
||||
if( treeParameters.getGenerateLeaves() ) {
|
||||
BillboardedLeavesMeshGenerator leafGen = new BillboardedLeavesMeshGenerator();
|
||||
leafMesh = leafGen.generateMesh(tips, treeParameters.getLeafScale());
|
||||
} else {
|
||||
leafMesh = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
// Set the new trunk
|
||||
updateTree(trunkMesh, leafMesh);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* ${Id}
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.scene.Spatial.CullHint;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
|
||||
|
||||
/**
|
||||
* Shows some sample people for scale.
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class AvatarState extends BaseAppState {
|
||||
|
||||
private Node avatars;
|
||||
private Spatial male;
|
||||
private Spatial female;
|
||||
|
||||
public AvatarState() {
|
||||
}
|
||||
|
||||
public void setShowAvatars( boolean b ) {
|
||||
if( b ) {
|
||||
avatars.setCullHint(CullHint.Inherit);
|
||||
} else {
|
||||
avatars.setCullHint(CullHint.Always);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
AssetManager assets = app.getAssetManager();
|
||||
|
||||
// Add an avatar for scale
|
||||
avatars = new Node("Avatars");
|
||||
avatars.move(2, 0, 0);
|
||||
|
||||
female = (Node)assets.loadModel("Models/female-parts.j3o");
|
||||
BoundingBox bb = (BoundingBox)female.getWorldBound();
|
||||
float height = bb.getYExtent() * 2;
|
||||
float femaleScale = 1.62f / height;
|
||||
female.move(0, bb.getYExtent(), 0);
|
||||
female.setLocalScale(femaleScale);
|
||||
Material mat = GuiGlobals.getInstance().createMaterial(ColorRGBA.Gray, true).getMaterial();
|
||||
mat.setColor("Ambient", ColorRGBA.Gray);
|
||||
female.setMaterial(mat);
|
||||
female.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
avatars.attachChild(female);
|
||||
|
||||
// Add an avatar for scale
|
||||
male = (Node)assets.loadModel("Models/male-parts-no-bones.j3o");
|
||||
bb = (BoundingBox)male.getWorldBound();
|
||||
height = bb.getYExtent() * 2;
|
||||
float maleScale = 1.77f / height;
|
||||
male.move(bb.getCenter().negate());
|
||||
male.move(1, bb.getYExtent(), 0);
|
||||
male.setLocalScale(maleScale);
|
||||
male.setMaterial(mat);
|
||||
male.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
avatars.attachChild(male);
|
||||
|
||||
// For testing
|
||||
//Spatial tree = assets.loadModel("Models/test1.j3o");
|
||||
//tree.setLocalTranslation(-20, 0, -20);
|
||||
//avatars.attachChild(tree);
|
||||
|
||||
|
||||
TreeOptionsState options = getState(TreeOptionsState.class);
|
||||
options.addOptionToggle("Avatars", this, "setShowAvatars").setChecked(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
Node rootNode = ((SimpleApplication)getApplication()).getRootNode();
|
||||
rootNode.attachChild(avatars);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
avatars.removeFromParent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.util.MemoryUtils;
|
||||
import com.simsilica.lemur.Container;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.HAlignment;
|
||||
import com.simsilica.lemur.Label;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import com.simsilica.lemur.input.InputMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class DebugHudState extends BaseAppState {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(DebugHudState.class);
|
||||
|
||||
private VersionedReference<Vector3f> worldLoc;
|
||||
private Runtime runtime = Runtime.getRuntime();
|
||||
|
||||
private Label location;
|
||||
private Label memory;
|
||||
private Label directMem;
|
||||
|
||||
private long lastUsedMem;
|
||||
private long lastMeg100;
|
||||
private long lastDirectMem;
|
||||
private long lastDirectMeg100;
|
||||
private long nextUpdate = System.currentTimeMillis() + 16; // 60 FPS max
|
||||
private long nextMemTime = System.currentTimeMillis() + 1000;
|
||||
|
||||
private long frameCounter;
|
||||
private long lastFrameCheck;
|
||||
private double lastFps;
|
||||
|
||||
private Container debugHud;
|
||||
|
||||
public DebugHudState() {
|
||||
}
|
||||
|
||||
public void toggleHud() {
|
||||
setEnabled( !isEnabled() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
// Always register for our hot key as long as
|
||||
// we are attached.
|
||||
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
|
||||
inputMapper.addDelegate( MainFunctions.F_HUD, this, "toggleHud" );
|
||||
|
||||
worldLoc = getState( MovementState.class ).getWorldPosition().createReference();
|
||||
|
||||
debugHud = new Container();
|
||||
|
||||
location = debugHud.addChild(new Label( "000.00 000.00 00.00" ));
|
||||
location.setTextHAlignment( HAlignment.Right );
|
||||
resetLocation();
|
||||
|
||||
memory = debugHud.addChild(new Label( "Mem: 0.0 meg (0.0 %)" ));
|
||||
memory.setTextHAlignment( HAlignment.Right );
|
||||
|
||||
directMem = debugHud.addChild(new Label( "DMem: 0.0 meg / 0" ));
|
||||
directMem.setTextHAlignment( HAlignment.Right );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
|
||||
inputMapper.removeDelegate( MainFunctions.F_HUD, this, "toggleHud" );
|
||||
}
|
||||
|
||||
protected void resetLocation() {
|
||||
Vector3f v = worldLoc.get();
|
||||
String loc = String.format( "%.2f, %.2f, %.2f", v.x, v.y, v.z );
|
||||
location.setText(loc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( float tpf ) {
|
||||
|
||||
frameCounter++;
|
||||
long time = System.currentTimeMillis();
|
||||
if( time < nextUpdate )
|
||||
return;
|
||||
nextUpdate = time + 16; // 60 FPS max
|
||||
|
||||
if( worldLoc.update() ) {
|
||||
resetLocation();
|
||||
}
|
||||
|
||||
/*if( time > lastFrameCheck + 1000 )
|
||||
{
|
||||
long delta = time - lastFrameCheck;
|
||||
lastFrameCheck = time;
|
||||
|
||||
double fps = frameCounter / (delta / 1000.0);
|
||||
frameCounter = 0;
|
||||
if( fps != lastFps )
|
||||
{
|
||||
lastFps = fps;
|
||||
String s = String.format( "FPS: %.2f", fps );
|
||||
fpsText.setText(s);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
// Refresh memory and other things less often----------------------------
|
||||
//-----------------------------------------------------------------------
|
||||
if( time < nextMemTime )
|
||||
return;
|
||||
nextMemTime = time + 1000;
|
||||
|
||||
long usedMemory = runtime.totalMemory() - runtime.freeMemory();
|
||||
if( lastUsedMem != usedMemory ) {
|
||||
lastUsedMem = usedMemory;
|
||||
|
||||
long maxMemory = runtime.maxMemory();
|
||||
long meg100 = (usedMemory * 100) / (1024 * 1024);
|
||||
if( lastMeg100 != meg100 ) {
|
||||
lastMeg100 = meg100;
|
||||
double meg = meg100 / 100.0;
|
||||
double percent = (usedMemory * 100.0 / maxMemory);
|
||||
String mem = String.format( "Mem: %.2f meg (%.1f %%)", meg, percent );
|
||||
memory.setText( mem );
|
||||
}
|
||||
}
|
||||
|
||||
long directUsage = MemoryUtils.getDirectMemoryUsage();
|
||||
if( directUsage != lastDirectMem ) {
|
||||
lastDirectMem = directUsage;
|
||||
|
||||
long meg100 = (directUsage * 100) / (1024 * 1024);
|
||||
if( lastDirectMeg100 != meg100 ) {
|
||||
long directCount = MemoryUtils.getDirectMemoryCount();
|
||||
double meg = meg100 / 100.0;
|
||||
String mem = String.format( "DMem: %.2f meg / %d", meg, directCount );
|
||||
directMem.setText( mem );
|
||||
}
|
||||
}
|
||||
|
||||
Camera cam = getApplication().getCamera();
|
||||
Vector3f pref = debugHud.getPreferredSize();
|
||||
debugHud.setLocalTranslation(cam.getWidth() - pref.x - 10, cam.getHeight() - 10, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
((SimpleApplication)getApplication()).getGuiNode().attachChild(debugHud);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
debugHud.removeFromParent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2013 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.bounding.BoundingSphere;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.BlendMode;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.post.Filter;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.renderer.Camera.FrustumIntersect;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.ViewPort;
|
||||
import com.jme3.renderer.queue.GeometryComparator;
|
||||
import com.jme3.renderer.queue.GeometryList;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.shadow.ShadowUtil;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class DropShadowFilter extends Filter {
|
||||
|
||||
private static final int VERTS_PER_SHADOW = 8; // one per box corner
|
||||
private static final int TRIS_PER_SHADOW = 12; // two per face
|
||||
private static final int INDEXES_PER_SHADOW = TRIS_PER_SHADOW * 3;
|
||||
|
||||
private static final Vector3f[] BASE_CORNERS = new Vector3f[] {
|
||||
new Vector3f(-1, -1, 1), // 0
|
||||
new Vector3f( 1, -1, 1), // 1
|
||||
new Vector3f( 1, -1, -1), // 2
|
||||
new Vector3f(-1, -1, -1), // 3
|
||||
new Vector3f(-1, 1, 1), // 4
|
||||
new Vector3f( 1, 1, 1), // 5
|
||||
new Vector3f( 1, 1, -1), // 6
|
||||
new Vector3f(-1, 1, -1) // 7
|
||||
};
|
||||
|
||||
private static final short[] BASE_INDEXES = new short[] {
|
||||
// top
|
||||
4, 5, 6, 4, 6, 7,
|
||||
// bottom
|
||||
3, 2, 1, 3, 1, 0,
|
||||
// +z
|
||||
0, 1, 5, 0, 5, 4,
|
||||
// -z
|
||||
2, 3, 7, 2, 7, 6,
|
||||
// -x
|
||||
3, 0, 4, 3, 4, 7,
|
||||
// +x
|
||||
1, 2, 6, 1, 6, 5
|
||||
};
|
||||
|
||||
private Geometry shadowGeom;
|
||||
private Material shadowMaterial;
|
||||
private Mesh mesh;
|
||||
private int maxShadows;
|
||||
|
||||
private ColorRGBA shadowColor = new ColorRGBA(0, 0, 0, 0.75f);
|
||||
|
||||
private VertexBuffer vbPos;
|
||||
private VertexBuffer vbNormal;
|
||||
private VertexBuffer vbTexCoord;
|
||||
private VertexBuffer vbTexCoord2;
|
||||
private VertexBuffer vbIndex;
|
||||
|
||||
private GeometryList casters;
|
||||
|
||||
public DropShadowFilter() {
|
||||
this(500);
|
||||
}
|
||||
|
||||
public DropShadowFilter( int maxShadows ) {
|
||||
this.maxShadows = maxShadows;
|
||||
}
|
||||
|
||||
public void setShadowIntensity( float f ) {
|
||||
shadowColor.a = f;
|
||||
}
|
||||
|
||||
public float getShadowIntensity() {
|
||||
return shadowColor.a;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isRequiresDepthTexture() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFilter(AssetManager assets, RenderManager rm, ViewPort vp, int w, int h) {
|
||||
|
||||
// Cheating... side effect of being lazy and using a filter
|
||||
// without actually needing to filter anything.
|
||||
material = new Material( assets, "MatDefs/Null.j3md" );
|
||||
|
||||
mesh = new Mesh();
|
||||
|
||||
// Setup the mesh for the max shadows size
|
||||
mesh.setBuffer( Type.Position, 3, BufferUtils.createVector3Buffer(maxShadows * VERTS_PER_SHADOW) );
|
||||
mesh.setBuffer( Type.Normal, 3, BufferUtils.createVector3Buffer(maxShadows * VERTS_PER_SHADOW) );
|
||||
mesh.setBuffer( Type.TexCoord, 3, BufferUtils.createVector3Buffer(maxShadows * VERTS_PER_SHADOW) );
|
||||
mesh.setBuffer( Type.TexCoord2, 3, BufferUtils.createVector3Buffer(maxShadows * VERTS_PER_SHADOW) );
|
||||
mesh.setBuffer( Type.Index, 3, BufferUtils.createShortBuffer(maxShadows * INDEXES_PER_SHADOW) );
|
||||
|
||||
vbPos = mesh.getBuffer(Type.Position);
|
||||
vbNormal = mesh.getBuffer(Type.Normal);
|
||||
vbTexCoord = mesh.getBuffer(Type.TexCoord);
|
||||
vbTexCoord2 = mesh.getBuffer(Type.TexCoord2);
|
||||
vbIndex = mesh.getBuffer(Type.Index);
|
||||
|
||||
|
||||
shadowGeom = new Geometry("shadowVolumes", mesh);
|
||||
Material m = shadowMaterial = new Material( assets, "MatDefs/Shadows.j3md" );
|
||||
m.setColor( "ShadowColor", shadowColor );
|
||||
m.getAdditionalRenderState().setDepthWrite(false);
|
||||
m.getAdditionalRenderState().setDepthTest(false);
|
||||
m.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||
shadowGeom.setMaterial(m);
|
||||
shadowGeom.setLocalTranslation(0, 100, 0);
|
||||
|
||||
shadowGeom.updateLogicalState(0.1f);
|
||||
shadowGeom.updateGeometricState();
|
||||
|
||||
// Set our custom comparator for shadow casters
|
||||
casters = new GeometryList(new CasterComparator());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Material getMaterial() {
|
||||
return material;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void postFrame( RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer ) {
|
||||
|
||||
RenderQueue rq = viewPort.getQueue();
|
||||
for (Spatial scene : viewPort.getScenes()) {
|
||||
//ShadowUtil.getGeometriesInCamFrustum(scene, viewPort.getCamera(), ShadowMode.Cast, casters);
|
||||
}
|
||||
if( casters.size() == 0 )
|
||||
return;
|
||||
|
||||
Camera cam = viewPort.getCamera();
|
||||
BoundingSphere cullCheck = new BoundingSphere();
|
||||
Vector3f pos = new Vector3f();
|
||||
|
||||
Texture frameTex = prevFilterBuffer.getColorBuffer().getTexture();
|
||||
Texture depthTex = prevFilterBuffer.getDepthBuffer().getTexture();
|
||||
shadowMaterial.setTexture("FrameTexture", frameTex);
|
||||
if( frameTex.getImage().getMultiSamples() > 1 ) {
|
||||
shadowMaterial.setInt("NumSamples", frameTex.getImage().getMultiSamples());
|
||||
} else {
|
||||
shadowMaterial.clearParam("NumSamples");
|
||||
}
|
||||
|
||||
shadowMaterial.setTexture("DepthTexture", depthTex);
|
||||
if( depthTex.getImage().getMultiSamples() > 1 ) {
|
||||
shadowMaterial.setInt("NumSamplesDepth", depthTex.getImage().getMultiSamples());
|
||||
} else {
|
||||
shadowMaterial.clearParam("NumSamplesDepth");
|
||||
}
|
||||
|
||||
int size = casters.size();
|
||||
if( size > maxShadows ) {
|
||||
// Give the shadows their best chance by sorting them.
|
||||
casters.setCamera(cam);
|
||||
casters.sort();
|
||||
}
|
||||
|
||||
FloatBuffer bPos = (FloatBuffer)vbPos.getData().rewind();
|
||||
FloatBuffer bNormal = (FloatBuffer)vbNormal.getData().rewind();
|
||||
FloatBuffer bTexCoord = (FloatBuffer)vbTexCoord.getData().rewind();
|
||||
FloatBuffer bTexCoord2 = (FloatBuffer)vbTexCoord2.getData().rewind();
|
||||
ShortBuffer bIndex = (ShortBuffer)vbIndex.getData().rewind();
|
||||
|
||||
|
||||
Matrix4f viewMatrix = cam.getViewMatrix();
|
||||
Matrix4f worldMatrix = new Matrix4f();
|
||||
Matrix4f worldViewMatrix = new Matrix4f();
|
||||
float[] angles = new float[3];
|
||||
Vector3f vTemp = new Vector3f();
|
||||
Vector3f vert = new Vector3f();
|
||||
Vector3f viewDir = new Vector3f();
|
||||
Vector3f boxScale = new Vector3f();
|
||||
|
||||
int rendered = 0;
|
||||
for( int i = 0; i < size; i++ ) {
|
||||
Geometry g = casters.get(i);
|
||||
|
||||
// Use the geometry bounds. We assumg it is still y-up
|
||||
// and merely rotated. It's a decent enough approximiation
|
||||
// in many cases and will produce better shadows for oblong
|
||||
// objects than a simple round radius would.
|
||||
BoundingBox bounds = (BoundingBox)g.getModelBound();
|
||||
|
||||
float scale = g.getWorldScale().x;
|
||||
float xEx = bounds.getXExtent() * scale;
|
||||
float yEx = bounds.getYExtent() * scale;
|
||||
float zEx = bounds.getZExtent() * scale;
|
||||
float volumeHeight = Math.max(yEx, Math.min(xEx,zEx));
|
||||
|
||||
float xOffset = bounds.getCenter().x * scale;
|
||||
float yOffset = bounds.getCenter().y * scale;
|
||||
float zOffset = bounds.getCenter().z * scale;
|
||||
|
||||
yOffset -= yEx;
|
||||
yOffset -= volumeHeight * 0.5f;
|
||||
yOffset += 0.01f;
|
||||
|
||||
pos.set(g.getWorldTranslation());
|
||||
pos.addLocal(xOffset, yOffset, zOffset);
|
||||
|
||||
// A conservative approximation that works because our shadow volume
|
||||
// is really just a round blob
|
||||
float radius = Math.max(xEx, Math.max(yEx, zEx));
|
||||
cullCheck.setCenter(pos);
|
||||
cullCheck.setRadius(radius);
|
||||
|
||||
int save = cam.getPlaneState();
|
||||
cam.setPlaneState(0);
|
||||
FrustumIntersect intersect = cam.contains(cullCheck);
|
||||
cam.setPlaneState(save);
|
||||
|
||||
if( intersect == FrustumIntersect.Outside ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boxScale.set(0.5f/xEx, 0.5f/volumeHeight, 0.5f/zEx);
|
||||
|
||||
Quaternion quat = g.getWorldRotation();
|
||||
angles = quat.toAngles(angles);
|
||||
|
||||
Quaternion rotation = new Quaternion().fromAngles(0, angles[1], 0);
|
||||
Quaternion invRotation = rotation.inverse();
|
||||
worldMatrix.setTranslation(pos);
|
||||
worldMatrix.setRotationQuaternion(rotation);
|
||||
|
||||
worldViewMatrix.set(viewMatrix);
|
||||
worldViewMatrix.multLocal(worldMatrix);
|
||||
|
||||
// Setup the vertexes for each corner
|
||||
for( int j = 0; j < VERTS_PER_SHADOW; j++ ) {
|
||||
vTemp.set(BASE_CORNERS[j].x * xEx,
|
||||
BASE_CORNERS[j].y * volumeHeight,
|
||||
BASE_CORNERS[j].z * zEx);
|
||||
|
||||
// Get the transformed coordinate in world space
|
||||
vert = worldMatrix.mult(vTemp, vert);
|
||||
bPos.put(vert.x).put(vert.y).put(vert.z);
|
||||
|
||||
// Now calculate the view direction
|
||||
vert = vert.subtractLocal(cam.getLocation());
|
||||
vert.normalizeLocal();
|
||||
viewDir = invRotation.mult(vert, viewDir);
|
||||
bNormal.put(viewDir.x).put(viewDir.y).put(viewDir.z);
|
||||
|
||||
// Model space is easy to calculate
|
||||
bTexCoord.put(BASE_CORNERS[j].x * xEx + xEx);
|
||||
bTexCoord.put(BASE_CORNERS[j].y * volumeHeight + volumeHeight);
|
||||
bTexCoord.put(BASE_CORNERS[j].z * zEx + zEx);
|
||||
|
||||
// And so is the scale... since it's always the same
|
||||
bTexCoord2.put(boxScale.x).put(boxScale.y).put(boxScale.z);
|
||||
}
|
||||
|
||||
// Fill in the index buffer
|
||||
for( int j = 0; j < INDEXES_PER_SHADOW; j++ ) {
|
||||
bIndex.put( (short)(BASE_INDEXES[j] + rendered * VERTS_PER_SHADOW) );
|
||||
}
|
||||
|
||||
rendered++;
|
||||
if( rendered >= maxShadows ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( rendered > 0 ) {
|
||||
// Need to zero out the left-overs
|
||||
for( int i = rendered; i < maxShadows; i++ ) {
|
||||
for( int j = 0; j < INDEXES_PER_SHADOW; j++ ) {
|
||||
bIndex.put((short)0);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the buffers
|
||||
bPos.rewind();
|
||||
bNormal.rewind();
|
||||
bTexCoord.rewind();
|
||||
bTexCoord2.rewind();
|
||||
bIndex.rewind();
|
||||
|
||||
vbPos.updateData(bPos);
|
||||
vbNormal.updateData(bNormal);
|
||||
vbTexCoord.updateData(bTexCoord);
|
||||
vbTexCoord2.updateData(bTexCoord2);
|
||||
vbIndex.updateData(bIndex);
|
||||
|
||||
shadowGeom.updateGeometricState();
|
||||
renderManager.renderGeometry(shadowGeom);
|
||||
}
|
||||
|
||||
casters.clear();
|
||||
}
|
||||
|
||||
private class CasterComparator implements GeometryComparator {
|
||||
|
||||
private Camera cam;
|
||||
private final Vector3f tempVec = new Vector3f();
|
||||
private final Vector3f tempVec2 = new Vector3f();
|
||||
|
||||
public void setCamera( Camera cam ) {
|
||||
this.cam = cam;
|
||||
}
|
||||
|
||||
public float distanceToCam( Geometry spat ) {
|
||||
if( spat == null ) {
|
||||
return Float.NEGATIVE_INFINITY;
|
||||
}
|
||||
|
||||
if( spat.queueDistance != Float.NEGATIVE_INFINITY ) {
|
||||
return spat.queueDistance;
|
||||
}
|
||||
|
||||
Vector3f camPosition = cam.getLocation();
|
||||
Vector3f viewVector = cam.getDirection(tempVec2);
|
||||
Vector3f spatPosition;
|
||||
|
||||
if( spat.getWorldBound() != null ) {
|
||||
spatPosition = spat.getWorldBound().getCenter();
|
||||
} else {
|
||||
spatPosition = spat.getWorldTranslation();
|
||||
}
|
||||
|
||||
spatPosition.subtract(camPosition, tempVec);
|
||||
spat.queueDistance = tempVec.dot(viewVector);
|
||||
|
||||
return spat.queueDistance;
|
||||
}
|
||||
|
||||
public int compare( Geometry o1, Geometry o2 ) {
|
||||
// Front to back sort
|
||||
float d1 = distanceToCam(o1);
|
||||
float d2 = distanceToCam(o2);
|
||||
|
||||
if( d1 == d2 ) {
|
||||
return 0;
|
||||
} else if( d1 < d2 ) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* ${Id}
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.export.binary.BinaryExporter;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.SceneGraphVisitorAdapter;
|
||||
import com.jme3.scene.Spatial;
|
||||
import com.jme3.system.JmeSystem;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.texture.Texture2D;
|
||||
import com.simsilica.lemur.Button;
|
||||
import com.simsilica.lemur.Command;
|
||||
import com.simsilica.lemur.Container;
|
||||
import com.simsilica.lemur.HAlignment;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.filechooser.FileFilter;
|
||||
import org.progeeks.json.JsonParser;
|
||||
import org.progeeks.json.JsonPrinter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Manages the file-related actions.
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class FileActionsState extends BaseAppState {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(FileActionsState.class);
|
||||
|
||||
private Container buttons;
|
||||
|
||||
public FileActionsState() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
buttons = new Container();
|
||||
|
||||
Button saveParms = buttons.addChild(new Button("Save Parms", "glass"));
|
||||
saveParms.addClickCommands(new SaveTreeParameters());
|
||||
saveParms.setTextHAlignment(HAlignment.Center);
|
||||
|
||||
Button loadParms = buttons.addChild(new Button("Load Parms", "glass"), 1);
|
||||
loadParms.addClickCommands(new LoadTreeParameters());
|
||||
loadParms.setTextHAlignment(HAlignment.Center);
|
||||
|
||||
Button saveJ3o = buttons.addChild(new Button("Export j3o", "glass"), 2);
|
||||
saveJ3o.addClickCommands(new SaveJ3o());
|
||||
saveJ3o.setTextHAlignment(HAlignment.Center);
|
||||
|
||||
Button saveTreeAtlas = buttons.addChild(new Button("Save Tree Atlas", "glass"));
|
||||
saveTreeAtlas.addClickCommands(new SaveTreeAtlas());
|
||||
saveTreeAtlas.setTextHAlignment(HAlignment.Center);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
getState(TreeOptionsState.class).getContents().addChild(buttons);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
getState(TreeOptionsState.class).getContents().removeChild(buttons);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out the stuff that we probably don't want to... or
|
||||
* really shouldn't be saving in a j3o. We should also remove
|
||||
* the Impostor LODs at least until there is a way to save and/or
|
||||
* embed the impostor image... but I don't right now.
|
||||
*/
|
||||
protected Spatial filterClone( Spatial tree ) {
|
||||
Spatial result = tree.deepClone();
|
||||
result.depthFirstTraversal(new SceneGraphVisitorAdapter() {
|
||||
@Override
|
||||
public void visit( Geometry g ) {
|
||||
if( g.getName().startsWith("wire:") ) {
|
||||
g.removeFromParent();
|
||||
}
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, File> lastRoots = new HashMap<String, File>();
|
||||
protected File chooseFile( final String description, final boolean save, String... extensions ) {
|
||||
//final String ext = (!extension.startsWith(".") ? "." : "") + extension.toLowerCase();
|
||||
final String[] exts = new String[extensions.length];
|
||||
for( int i = 0; i < exts.length; i++ ) {
|
||||
exts[i] = (!extensions[i].startsWith(".") ? "." : "") + extensions[i].toLowerCase();
|
||||
}
|
||||
|
||||
File lastRoot = lastRoots.get(exts[0]);
|
||||
if( lastRoot == null ) {
|
||||
lastRoot = new File(".");
|
||||
}
|
||||
|
||||
log.info("Creating file chooser dialog...");
|
||||
final JFileChooser openDialog = new JFileChooser();
|
||||
|
||||
openDialog.setDialogTitle("Choose Location");
|
||||
if( save ) {
|
||||
openDialog.setDialogType(JFileChooser.SAVE_DIALOG);
|
||||
} else {
|
||||
openDialog.setDialogType(JFileChooser.OPEN_DIALOG);
|
||||
}
|
||||
openDialog.setFileFilter(new FileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept( File file ) {
|
||||
if( file.isDirectory() ) {
|
||||
return true;
|
||||
}
|
||||
String s = file.getName().toLowerCase();
|
||||
for( String e : exts ) {
|
||||
if( s.endsWith(e) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
});
|
||||
openDialog.setCurrentDirectory(lastRoot);
|
||||
|
||||
log.info("Opening file chooser dialog...");
|
||||
|
||||
final int[] dialogResult = new int[1]; //JFileChooser.CANCEL_OPTION ;
|
||||
if( !SwingUtilities.isEventDispatchThread() ) {
|
||||
try {
|
||||
SwingUtilities.invokeAndWait( new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if( save ) {
|
||||
dialogResult[0] = openDialog.showSaveDialog(null);
|
||||
} else {
|
||||
dialogResult[0] = openDialog.showOpenDialog(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch( Exception e ) {
|
||||
throw new RuntimeException("Error invoking", e);
|
||||
}
|
||||
} else {
|
||||
if( save ) {
|
||||
dialogResult[0] = openDialog.showSaveDialog(null);
|
||||
} else {
|
||||
dialogResult[0] = openDialog.showOpenDialog(null);
|
||||
}
|
||||
}
|
||||
|
||||
if( dialogResult[0] != JFileChooser.APPROVE_OPTION ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File result = openDialog.getSelectedFile();
|
||||
lastRoots.put(exts[0], result.getParentFile());
|
||||
|
||||
if( save && !result.getName().toLowerCase().endsWith(exts[0]) ) {
|
||||
result = new File(result.getParent(), result.getName() + exts[0]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void writeJson( File f, Map<String, Object> map ) throws IOException {
|
||||
|
||||
FileWriter out = new FileWriter(f);
|
||||
try {
|
||||
JsonPrinter json = new JsonPrinter();
|
||||
json.write(map, out);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected Map<String, Object> readJson( File f ) throws IOException {
|
||||
FileReader in = new FileReader(f);
|
||||
try {
|
||||
JsonParser json = new JsonParser();
|
||||
return (Map<String, Object>)json.parse(in);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveJ3o( File f ) throws IOException {
|
||||
BinaryExporter exporter = BinaryExporter.getInstance();
|
||||
log.info("Writing:" + f);
|
||||
exporter.save(filterClone(getState(ForestGridState.class).getMainTreeNode()), f);
|
||||
}
|
||||
|
||||
public void saveTreeParameters( File f ) throws IOException {
|
||||
TreeParameters treeParameters = getState(TreeParametersState.class).getTreeParameters();
|
||||
Map<String, Object> map = treeParameters.toMap();
|
||||
log.info("Writing:" + f);
|
||||
writeJson(f, map);
|
||||
}
|
||||
|
||||
public void loadTreeParameters( File f ) throws IOException {
|
||||
Map<String, Object> map = readJson(f);
|
||||
TreeParameters treeParameters = getState(TreeParametersState.class).getTreeParameters();
|
||||
treeParameters.fromMap(map);
|
||||
getState(TreeParametersState.class).refreshTreePanels();
|
||||
getState(ForestGridState.class).rebuild();
|
||||
}
|
||||
|
||||
public void savePng( File f, Image img ) throws IOException {
|
||||
OutputStream out = new FileOutputStream(f);
|
||||
try {
|
||||
JmeSystem.writeImageFile(out, "png", img.getData(0), img.getWidth(), img.getHeight());
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void saveTreeAtlas( File f ) throws IOException {
|
||||
|
||||
Image diffuse = getState(AtlasGeneratorState.class).getDiffuseMap();
|
||||
savePng(f, diffuse);
|
||||
|
||||
String normalName = f.getName();
|
||||
if( normalName.toLowerCase().endsWith(".png") ) {
|
||||
normalName = normalName.substring(0, normalName.length() - ".png".length());
|
||||
}
|
||||
f = new File(f.getParentFile(), normalName + "-normals.png");
|
||||
|
||||
Image normal = getState(AtlasGeneratorState.class).getNormalMap();
|
||||
savePng(f, normal);
|
||||
}
|
||||
|
||||
private class SaveJ3o implements Command<Button> {
|
||||
|
||||
@Override
|
||||
public void execute( Button source ) {
|
||||
System.out.println( "Saving j3o..." );
|
||||
File f = chooseFile("JME object file", true, "j3o");
|
||||
System.out.println( "File:" + f );
|
||||
if( f == null ) {
|
||||
return;
|
||||
}
|
||||
if( f.exists() ) {
|
||||
int result = JOptionPane.showConfirmDialog(null, "Overwrite file?\n" + f,
|
||||
"File Already Exists",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
if( result != JOptionPane.YES_OPTION ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
saveJ3o(f);
|
||||
} catch( IOException e ) {
|
||||
log.error( "Error saving tree to:" + f, e );
|
||||
JmeSystem.showErrorDialog("Error writing file:" + f + "\n" + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SaveTreeParameters implements Command<Button> {
|
||||
|
||||
@Override
|
||||
public void execute( Button source ) {
|
||||
System.out.println( "Saving stparms.json..." );
|
||||
File f = chooseFile("Tree Parameters File", true, "simap");
|
||||
System.out.println( "File:" + f );
|
||||
if( f == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if( f.exists() ) {
|
||||
int result = JOptionPane.showConfirmDialog(null, "Overwrite file?\n" + f,
|
||||
"File Already Exists",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
if( result != JOptionPane.YES_OPTION ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
saveTreeParameters(f);
|
||||
} catch( IOException e ) {
|
||||
log.error("Error writing file:" + f, e);
|
||||
JmeSystem.showErrorDialog("Error writing file:" + f + "\n" + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadTreeParameters implements Command<Button> {
|
||||
|
||||
@Override
|
||||
public void execute( Button source ) {
|
||||
System.out.println( "Loading stparms.json..." );
|
||||
File f = chooseFile("Tree Parameters File", false, "simap", "simap.json");
|
||||
System.out.println( "File:" + f );
|
||||
if( f == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
loadTreeParameters(f);
|
||||
} catch( IOException e ) {
|
||||
log.error("Error reading file:" + f, e);
|
||||
JmeSystem.showErrorDialog("Error reading file:" + f + "\n" + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class SaveTreeAtlas implements Command<Button> {
|
||||
|
||||
@Override
|
||||
public void execute( Button source ) {
|
||||
|
||||
File f = chooseFile("Tree Atlas Images", true, "png");
|
||||
if( f == null ) {
|
||||
return;
|
||||
}
|
||||
if( f.exists() ) {
|
||||
int result = JOptionPane.showConfirmDialog(null, "Overwrite file?\n" + f,
|
||||
"File Already Exists",
|
||||
JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
if( result != JOptionPane.YES_OPTION ) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
saveTreeAtlas(f);
|
||||
} catch( IOException e ) {
|
||||
log.error("Error writing file:" + f, e);
|
||||
JmeSystem.showErrorDialog("Error writing file:" + f + "\n" + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
369
simarboreal/src/main/java/com/simsilica/arboreal/ForestGrid.java
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.scene.Node;
|
||||
import com.simsilica.builder.Builder;
|
||||
import java.util.Random;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class ForestGrid {
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
private float spacing;
|
||||
private int rootSeed;
|
||||
private int seedRange;
|
||||
|
||||
private float rotationVariation = 1f;
|
||||
private float leanVariation = 0.1f;
|
||||
private float scaleVariation = 0.3f;
|
||||
private float positionVariation = 0;
|
||||
|
||||
private Node root;
|
||||
|
||||
private Builder builder;
|
||||
private TreeBuilderReference[][] trees;
|
||||
|
||||
private TreeParameters treeParameters;
|
||||
private Material treeMaterial;
|
||||
private Material wireMaterial;
|
||||
private Material leafMaterial;
|
||||
private Material flatMaterial;
|
||||
private Material impostorMaterial;
|
||||
|
||||
private boolean showWireframe;
|
||||
|
||||
public ForestGrid( TreeParameters treeParameters,
|
||||
Material treeMaterial,
|
||||
Material wireMaterial,
|
||||
Material leafMaterial,
|
||||
Material flatMaterial,
|
||||
Material impostorMaterial,
|
||||
Builder builder ) {
|
||||
this.treeParameters = treeParameters;
|
||||
this.treeMaterial = treeMaterial;
|
||||
this.wireMaterial = wireMaterial;
|
||||
this.leafMaterial = leafMaterial;
|
||||
this.flatMaterial = flatMaterial;
|
||||
this.impostorMaterial = impostorMaterial;
|
||||
this.builder = builder;
|
||||
this.root = new Node("Forest");
|
||||
this.spacing = 5;
|
||||
this.seedRange = 9;
|
||||
setSize(1, 1);
|
||||
}
|
||||
|
||||
public Node getRootNode() {
|
||||
return root;
|
||||
}
|
||||
|
||||
public TreeBuilderReference getTree( int i, int j ) {
|
||||
return trees[i][j];
|
||||
}
|
||||
|
||||
public void setShowWireframe( boolean b ) {
|
||||
if( this.showWireframe == b ) {
|
||||
return;
|
||||
}
|
||||
this.showWireframe = b;
|
||||
refreshWireframe();
|
||||
}
|
||||
|
||||
public void setSeedRange( int range ) {
|
||||
if( this.seedRange == range ) {
|
||||
return;
|
||||
}
|
||||
this.seedRange = range;
|
||||
refreshSeed();
|
||||
}
|
||||
|
||||
public int getSeedRange() {
|
||||
return seedRange;
|
||||
}
|
||||
|
||||
public void setSpacing( float f ) {
|
||||
if( this.spacing == f ) {
|
||||
return;
|
||||
}
|
||||
this.spacing = f;
|
||||
refreshVariation();
|
||||
}
|
||||
|
||||
public float getSpacing() {
|
||||
return spacing;
|
||||
}
|
||||
|
||||
public void setRotationVariation( float f ) {
|
||||
if( this.rotationVariation == f ) {
|
||||
return;
|
||||
}
|
||||
this.rotationVariation = f;
|
||||
refreshVariation();
|
||||
}
|
||||
|
||||
public float getRotationVariation() {
|
||||
return rotationVariation;
|
||||
}
|
||||
|
||||
public void setLeanVariation( float f ) {
|
||||
if( this.leanVariation == f ) {
|
||||
return;
|
||||
}
|
||||
this.leanVariation = f;
|
||||
refreshVariation();
|
||||
}
|
||||
|
||||
public float getLeanVariation() {
|
||||
return leanVariation;
|
||||
}
|
||||
|
||||
public void setScaleVariation( float f ) {
|
||||
if( this.scaleVariation == f ) {
|
||||
return;
|
||||
}
|
||||
this.scaleVariation = f;
|
||||
refreshVariation();
|
||||
}
|
||||
|
||||
public float getScaleVariation() {
|
||||
return scaleVariation;
|
||||
}
|
||||
|
||||
public void setPositionVariation( float f ) {
|
||||
if( this.positionVariation == f ) {
|
||||
return;
|
||||
}
|
||||
this.positionVariation = f;
|
||||
refreshVariation();
|
||||
}
|
||||
|
||||
public float getPositionVariation() {
|
||||
return positionVariation;
|
||||
}
|
||||
|
||||
|
||||
public void setWidth( int width ) {
|
||||
setSize(width, height);
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setHeight( int height ) {
|
||||
setSize(width, height);
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setSize( int width, int height ) {
|
||||
|
||||
if( this.width == width && this.height == height ) {
|
||||
return;
|
||||
}
|
||||
|
||||
builder.pause();
|
||||
|
||||
if( trees != null ) {
|
||||
// Cancel and remove the ones that will go away
|
||||
if( this.width > width ) {
|
||||
for( int i = width; i < this.width; i++ ) {
|
||||
for( int j = 0; j < this.height; j++ ) {
|
||||
trees[i][j].getTreeNode().removeFromParent();
|
||||
builder.release(trees[i][j]);
|
||||
trees[i][j] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if( this.height > height ) {
|
||||
for( int i = 0; i < this.width; i++ ) {
|
||||
for( int j = height; j < this.height; j++ ) {
|
||||
trees[i][j].getTreeNode().removeFromParent();
|
||||
builder.release(trees[i][j]);
|
||||
trees[i][j] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy what we can into a new structure
|
||||
TreeBuilderReference[][] newTrees = new TreeBuilderReference[width][height];
|
||||
int w = Math.min(width, this.width);
|
||||
int h = Math.min(height, this.height);
|
||||
for( int i = 0; i < w; i++ ) {
|
||||
for( int j = 0; j < h; j++ ) {
|
||||
newTrees[i][j] = trees[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
|
||||
trees = newTrees;
|
||||
|
||||
// Fill in any missing cells
|
||||
for( int i = 0; i < width; i++ ) {
|
||||
for( int j = 0; j < height; j++ ) {
|
||||
if( trees[i][j] == null ) {
|
||||
trees[i][j] = new TreeBuilderReference(treeParameters,
|
||||
treeMaterial,
|
||||
wireMaterial,
|
||||
leafMaterial,
|
||||
flatMaterial,
|
||||
impostorMaterial);
|
||||
Node tree = trees[i][j].getTreeNode();
|
||||
tree.setLocalTranslation(i * spacing, 0, j * spacing);
|
||||
tree.setLocalScale(treeParameters.getBaseScale());
|
||||
root.attachChild(tree);
|
||||
builder.build(trees[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refreshSeed();
|
||||
refreshWireframe();
|
||||
refreshVariation();
|
||||
|
||||
builder.resume();
|
||||
}
|
||||
|
||||
public void markChanged() {
|
||||
for( int i = 0; i < width; i++ ) {
|
||||
for( int j = 0; j < height; j++ ) {
|
||||
if( trees[i][j] == null ) {
|
||||
continue;
|
||||
}
|
||||
trees[i][j].markChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// Because it's a value we sort of cache and there
|
||||
// is no external way to detect it's changed.
|
||||
refreshSeed();
|
||||
}
|
||||
|
||||
public void rebuild() {
|
||||
for( int i = 0; i < width; i++ ) {
|
||||
for( int j = 0; j < height; j++ ) {
|
||||
if( trees[i][j] == null ) {
|
||||
continue;
|
||||
}
|
||||
builder.build(trees[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshWireframe() {
|
||||
for( int i = 0; i < width; i++ ) {
|
||||
for( int j = 0; j < height; j++ ) {
|
||||
if( trees[i][j] == null ) {
|
||||
continue;
|
||||
}
|
||||
trees[i][j].setWireFrame(showWireframe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshSeed() {
|
||||
int index = 0;
|
||||
int rootSeed = treeParameters.getSeed();
|
||||
for( int i = 0; i < width; i++ ) {
|
||||
for( int j = 0; j < height; j++ ) {
|
||||
if( trees[i][j] == null ) {
|
||||
continue;
|
||||
}
|
||||
trees[i][j].setSeed(rootSeed + (index % seedRange));
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshVariation() {
|
||||
|
||||
// If there is only one tree then we won't vary it
|
||||
// at all... we could also have made sure 0,0 was always
|
||||
// 0 variation but I think this will be ok
|
||||
if( width == 1 && height == 1 ) {
|
||||
// Just make sure it doesn't have anything weird
|
||||
Node tree = trees[0][0].getTreeNode();
|
||||
tree.setLocalScale(treeParameters.getBaseScale());
|
||||
tree.setLocalRotation(new Quaternion());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Random rand = new Random(0);
|
||||
for( int i = 0; i < width; i++ ) {
|
||||
for( int j = 0; j < height; j++ ) {
|
||||
if( trees[i][j] == null ) {
|
||||
continue;
|
||||
}
|
||||
Node tree = trees[i][j].getTreeNode();
|
||||
float x = leanVariation * ((rand.nextFloat() * 2) - 1) * FastMath.QUARTER_PI;
|
||||
float y = leanVariation * ((rand.nextFloat() * 2) - 1) * FastMath.QUARTER_PI;
|
||||
float scale = scaleVariation * ((rand.nextFloat() * 2) - 1);
|
||||
float angle = rotationVariation * ((rand.nextFloat() * 2) - 1) * FastMath.TWO_PI;
|
||||
|
||||
Quaternion rot = new Quaternion();
|
||||
rot = rot.mult(new Quaternion().fromAngles(0, angle, 0));
|
||||
rot = rot.mult(new Quaternion().fromAngles(x, 0, 0));
|
||||
rot = rot.mult(new Quaternion().fromAngles(0, 0, y));
|
||||
|
||||
tree.setLocalRotation(rot);
|
||||
|
||||
if( scale < 0 )
|
||||
scale *= 0.5f;
|
||||
tree.setLocalScale((1 + scale) * treeParameters.getBaseScale());
|
||||
|
||||
float xOffset = positionVariation * (rand.nextFloat() - 0.5f) * spacing;
|
||||
float yOffset = positionVariation * (rand.nextFloat() - 0.5f) * spacing;
|
||||
tree.setLocalTranslation(i * spacing + xOffset, 0, j * spacing + yOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.simsilica.builder.BuilderState;
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.BlendMode;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector4f;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.simsilica.fx.sky.SkyState;
|
||||
import com.simsilica.lemur.Checkbox;
|
||||
import com.simsilica.lemur.Container;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.Label;
|
||||
import com.simsilica.lemur.component.SpringGridLayout;
|
||||
import com.simsilica.lemur.core.VersionedHolder;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import com.simsilica.lemur.props.PropertyPanel;
|
||||
import com.simsilica.lemur.style.ElementId;
|
||||
|
||||
|
||||
/**
|
||||
* Manages a ForestGrid instance as well as hooking up
|
||||
* the UI and materials necessary for it to work.
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class ForestGridState extends BaseAppState {
|
||||
|
||||
private ForestGrid forestGrid;
|
||||
private TreeBuilderReference mainTree;
|
||||
|
||||
private Label vertsLabel;
|
||||
private Label trisLabel;
|
||||
private VersionedHolder<Boolean> building = new VersionedHolder<Boolean>(true);
|
||||
|
||||
private VersionedReference<TreeParameters> treeParameters;
|
||||
private VersionedReference<PropertyPanel> gridParameters;
|
||||
|
||||
private Texture bark;
|
||||
private Texture barkNormals;
|
||||
private Texture barkBumps;
|
||||
private Texture leafAtlas;
|
||||
private Texture testPattern;
|
||||
private Texture noise;
|
||||
|
||||
private Material treeMaterial;
|
||||
private Material wireMaterial;
|
||||
private Material flatMaterial;
|
||||
private Material impostorMaterial;
|
||||
private Material leafMaterial;
|
||||
|
||||
private boolean showTestPattern = false;
|
||||
private boolean showTrunkBumps = true;
|
||||
private boolean useWind = false;
|
||||
private boolean useScattering = false;
|
||||
|
||||
private Vector4f windCurve = new Vector4f();
|
||||
|
||||
public ForestGridState() {
|
||||
}
|
||||
|
||||
public VersionedReference<Boolean> getBuildingRef() {
|
||||
return building.createReference();
|
||||
}
|
||||
|
||||
public TreeBuilderReference getMainTree() {
|
||||
return mainTree;
|
||||
}
|
||||
|
||||
public Node getMainTreeNode() {
|
||||
return mainTree.getTreeNode();
|
||||
}
|
||||
|
||||
public void rebuild() {
|
||||
building.setObject(true);
|
||||
forestGrid.markChanged();
|
||||
forestGrid.rebuild();
|
||||
}
|
||||
|
||||
public void setShowTestPattern( boolean b ) {
|
||||
if( this.showTestPattern == b ) {
|
||||
return;
|
||||
}
|
||||
this.showTestPattern = b;
|
||||
if( showTestPattern ) {
|
||||
treeMaterial.setTexture("DiffuseMap", testPattern);
|
||||
flatMaterial.setTexture("DiffuseMap", testPattern);
|
||||
} else {
|
||||
treeMaterial.setTexture("DiffuseMap", bark);
|
||||
flatMaterial.setTexture("DiffuseMap", bark);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getShowTestPattern() {
|
||||
return showTestPattern;
|
||||
}
|
||||
|
||||
public void setShowTrunkBumps( boolean b ) {
|
||||
if( this.showTrunkBumps == b ) {
|
||||
return;
|
||||
}
|
||||
this.showTrunkBumps = b;
|
||||
if( showTrunkBumps ) {
|
||||
treeMaterial.setTexture("NormalMap", barkNormals);
|
||||
treeMaterial.setTexture("ParallaxMap", barkBumps);
|
||||
} else {
|
||||
treeMaterial.clearParam("NormalMap");
|
||||
treeMaterial.clearParam("ParallaxMap");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getShowTrunkBumps() {
|
||||
return showTrunkBumps;
|
||||
}
|
||||
|
||||
public void setUseWind( boolean b ) {
|
||||
if( this.useWind == b ) {
|
||||
return;
|
||||
}
|
||||
this.useWind = b;
|
||||
treeMaterial.setBoolean("UseWind", useWind);
|
||||
leafMaterial.setBoolean("UseWind", useWind);
|
||||
flatMaterial.setBoolean("UseWind", useWind);
|
||||
impostorMaterial.setBoolean("UseWind", useWind);
|
||||
}
|
||||
|
||||
public boolean getUseWind() {
|
||||
return useWind;
|
||||
}
|
||||
|
||||
public void setUseScattering( boolean b ) {
|
||||
if( this.useScattering == b ) {
|
||||
return;
|
||||
}
|
||||
this.useScattering = b;
|
||||
if( treeMaterial != null ) {
|
||||
treeMaterial.setBoolean("UseScattering", useScattering);
|
||||
leafMaterial.setBoolean("UseScattering", useScattering);
|
||||
flatMaterial.setBoolean("UseScattering", useScattering);
|
||||
impostorMaterial.setBoolean("UseScattering", useScattering);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
AssetManager assets = app.getAssetManager();
|
||||
|
||||
bark = assets.loadTexture("Textures/bark128.jpg");
|
||||
bark.setWrap(Texture.WrapMode.Repeat);
|
||||
barkNormals = assets.loadTexture("Textures/bark128-norm.jpg");
|
||||
barkNormals.setWrap(Texture.WrapMode.Repeat);
|
||||
barkBumps = assets.loadTexture("Textures/bark128-bump.png");
|
||||
barkBumps.setWrap(Texture.WrapMode.Repeat);
|
||||
|
||||
testPattern = assets.loadTexture("Textures/test-pattern.png");
|
||||
testPattern.setWrap(Texture.WrapMode.Repeat);
|
||||
|
||||
leafAtlas = assets.loadTexture("Textures/leaf-atlas.png");
|
||||
leafAtlas.setWrap(Texture.WrapMode.Repeat);
|
||||
|
||||
noise = assets.loadTexture("Textures/noise-x3-512.png");
|
||||
noise.setWrap(Texture.WrapMode.Repeat);
|
||||
|
||||
treeParameters = getState(TreeParametersState.class).getTreeParametersRef();
|
||||
|
||||
forestGrid = new ForestGrid(treeParameters.get(),
|
||||
getTreeMaterial(),
|
||||
getWireMaterial(),
|
||||
getLeafMaterial(),
|
||||
getFlatMaterial(),
|
||||
getImpostorMaterial(),
|
||||
getState(BuilderState.class).getBuilder());
|
||||
|
||||
mainTree = forestGrid.getTree(0, 0);
|
||||
|
||||
|
||||
// Add some options check boxes for rendering
|
||||
TreeOptionsState options = getState(TreeOptionsState.class);
|
||||
options.addOptionToggle("Wireframe", forestGrid, "setShowWireframe");
|
||||
options.addOptionToggle("Test Pattern", this, "setShowTestPattern");
|
||||
Checkbox cb = options.addOptionToggle("Bump-map", this, "setShowTrunkBumps");
|
||||
cb.setChecked(true);
|
||||
|
||||
options.addOptionToggle("Wind", this, "setUseWind");
|
||||
|
||||
PropertyPanel properties = new PropertyPanel("glass");
|
||||
gridParameters = properties.createReference();
|
||||
options.getParameterTabs().addTab("Grid", properties);
|
||||
|
||||
properties.addIntProperty("Width", forestGrid, "width", 1, 10, 1);
|
||||
properties.addIntProperty("Height", forestGrid, "height", 1, 10, 1);
|
||||
properties.addFloatProperty("Spacing (m)", forestGrid, "spacing", 0.3f, 40, 0.1f);
|
||||
properties.addIntProperty("Seed Range", forestGrid, "seedRange", 1, 100, 1);
|
||||
properties.addFloatProperty("Rotation Variation (*)", forestGrid, "rotationVariation", 0, 1, 0.01f);
|
||||
properties.addFloatProperty("Lean Variation (*)", forestGrid, "leanVariation", 0, 1, 0.01f);
|
||||
properties.addFloatProperty("Scale Variation (*)", forestGrid, "scaleVariation", 0, 1, 0.01f);
|
||||
properties.addFloatProperty("Position Variation (*)", forestGrid, "positionVariation", 0, 1, 0.01f);
|
||||
|
||||
|
||||
// Add a stats panel to the bottom... could have done it as another
|
||||
// state but we already manage all of the geometry here
|
||||
Container stats = options.getContents().addChild(new Container(new SpringGridLayout(), new ElementId("stats"), "glass"));
|
||||
vertsLabel = stats.addChild(new Label("verts:", "glass"));
|
||||
trisLabel = stats.addChild(new Label("tris:", "glass"), 1);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
Node rootNode = ((SimpleApplication)getApplication()).getRootNode();
|
||||
rootNode.attachChild(forestGrid.getRootNode());
|
||||
}
|
||||
|
||||
private float nextUpdateCheck = 0.1f;
|
||||
private float time;
|
||||
@Override
|
||||
public void update( float tpf ) {
|
||||
|
||||
|
||||
// Calculate the wind curves
|
||||
time += tpf;
|
||||
windCurve.x = time;
|
||||
|
||||
nextUpdateCheck += tpf;
|
||||
if( nextUpdateCheck <= 0.1f ) {
|
||||
return;
|
||||
}
|
||||
nextUpdateCheck = 0;
|
||||
|
||||
boolean changed = treeParameters.update();
|
||||
if( gridParameters.update() ) {
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if( changed ) {
|
||||
building.setObject(true);
|
||||
forestGrid.markChanged();
|
||||
forestGrid.rebuild();
|
||||
refreshStats();
|
||||
refreshWindParms();
|
||||
}
|
||||
|
||||
if( building.getObject() && getState(BuilderState.class).getBuilder().getPending() == 0 ) {
|
||||
building.setObject(false);
|
||||
refreshStats();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
forestGrid.getRootNode().removeFromParent();
|
||||
}
|
||||
|
||||
protected void refreshStats() {
|
||||
if( building.getObject() ) {
|
||||
vertsLabel.setText("verts: ???");
|
||||
trisLabel.setText("tris: ???");
|
||||
} else {
|
||||
vertsLabel.setText("verts: " + mainTree.getVertexCount());
|
||||
trisLabel.setText("tris: " + mainTree.getTriangleCount());
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshWindParms() {
|
||||
if( treeMaterial == null ) {
|
||||
// too soon
|
||||
return;
|
||||
}
|
||||
TreeParameters tp = treeParameters.get();
|
||||
|
||||
treeMaterial.setFloat("FlexHeight", tp.getFlexHeight());
|
||||
treeMaterial.setFloat("TrunkFlexibility", tp.getTrunkFlexibility());
|
||||
treeMaterial.setFloat("BranchFlexibility", tp.getBranchFlexibility());
|
||||
|
||||
leafMaterial.setFloat("FlexHeight", tp.getFlexHeight());
|
||||
leafMaterial.setFloat("TrunkFlexibility", tp.getTrunkFlexibility());
|
||||
leafMaterial.setFloat("BranchFlexibility", tp.getBranchFlexibility());
|
||||
|
||||
flatMaterial.setFloat("FlexHeight", tp.getFlexHeight());
|
||||
flatMaterial.setFloat("TrunkFlexibility", tp.getTrunkFlexibility());
|
||||
flatMaterial.setFloat("BranchFlexibility", tp.getBranchFlexibility());
|
||||
|
||||
impostorMaterial.setFloat("TrunkFlexibility", tp.getTrunkFlexibility());
|
||||
|
||||
}
|
||||
|
||||
public Material getTreeMaterial() {
|
||||
if( treeMaterial != null ) {
|
||||
return treeMaterial;
|
||||
}
|
||||
|
||||
treeMaterial = GuiGlobals.getInstance().createMaterial(ColorRGBA.Yellow, true).getMaterial();
|
||||
treeMaterial = new Material(getApplication().getAssetManager(), "MatDefs/TreeLighting.j3md");
|
||||
treeMaterial.setColor("Diffuse", ColorRGBA.White);
|
||||
treeMaterial.setColor("Ambient", ColorRGBA.White);
|
||||
treeMaterial.setBoolean("UseMaterialColors", true);
|
||||
treeMaterial.setBoolean("UseWind", false);
|
||||
treeMaterial.setTexture("WindNoise", noise);
|
||||
treeMaterial.setTexture("DiffuseMap", bark);
|
||||
treeMaterial.setTexture("NormalMap", barkNormals);
|
||||
treeMaterial.setTexture("ParallaxMap", barkBumps);
|
||||
//treeMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
|
||||
|
||||
// Hook it up to the atmospherics
|
||||
getState(SkyState.class).getAtmosphericParameters().applyGroundParameters(treeMaterial, true);
|
||||
|
||||
return treeMaterial;
|
||||
}
|
||||
|
||||
public Material getFlatMaterial() {
|
||||
if( flatMaterial != null ) {
|
||||
return flatMaterial;
|
||||
}
|
||||
|
||||
flatMaterial = new Material(getApplication().getAssetManager(), "MatDefs/AxisBillboardLighting.j3md");
|
||||
flatMaterial.setColor("Diffuse", ColorRGBA.White);
|
||||
flatMaterial.setColor("Ambient", ColorRGBA.White);
|
||||
flatMaterial.setBoolean("UseMaterialColors", true);
|
||||
flatMaterial.setTexture("DiffuseMap", bark);
|
||||
flatMaterial.setBoolean("UseWind", false);
|
||||
flatMaterial.setTexture("WindNoise", noise);
|
||||
flatMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
|
||||
|
||||
// Hook it up to the atmospherics
|
||||
getState(SkyState.class).getAtmosphericParameters().applyGroundParameters(flatMaterial, true);
|
||||
|
||||
return flatMaterial;
|
||||
}
|
||||
|
||||
public Material getImpostorMaterial() {
|
||||
if( impostorMaterial != null ) {
|
||||
return impostorMaterial;
|
||||
}
|
||||
|
||||
impostorMaterial = new Material(getApplication().getAssetManager(), "MatDefs/IndexedBillboardLighting.j3md");
|
||||
impostorMaterial.setColor("Diffuse", ColorRGBA.White);
|
||||
impostorMaterial.setColor("Ambient", ColorRGBA.White);
|
||||
impostorMaterial.setBoolean("UseMaterialColors", true);
|
||||
impostorMaterial.setFloat("AlphaDiscardThreshold", 0.5f);
|
||||
|
||||
impostorMaterial.setBoolean("UseWind", false);
|
||||
impostorMaterial.setTexture("WindNoise", noise);
|
||||
|
||||
impostorMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
|
||||
impostorMaterial.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||
|
||||
// Hook it up to the atmospherics
|
||||
getState(SkyState.class).getAtmosphericParameters().applyGroundParameters(impostorMaterial, true);
|
||||
|
||||
return impostorMaterial;
|
||||
}
|
||||
|
||||
public Material getWireMaterial() {
|
||||
if( wireMaterial != null ) {
|
||||
return wireMaterial;
|
||||
}
|
||||
Material mat = GuiGlobals.getInstance().createMaterial(ColorRGBA.Yellow, false).getMaterial();
|
||||
wireMaterial = mat;
|
||||
mat.getAdditionalRenderState().setWireframe(true);
|
||||
return mat;
|
||||
}
|
||||
|
||||
public Material getLeafMaterial() {
|
||||
if( leafMaterial != null ) {
|
||||
return leafMaterial;
|
||||
}
|
||||
|
||||
AssetManager assets = getApplication().getAssetManager();
|
||||
leafMaterial = new Material(assets, "MatDefs/LeafLighting.j3md");
|
||||
leafMaterial.setColor("Diffuse", ColorRGBA.White);
|
||||
leafMaterial.setColor("Ambient", ColorRGBA.White);
|
||||
leafMaterial.setBoolean("UseMaterialColors", true);
|
||||
leafMaterial.setTexture("DiffuseMap", leafAtlas);
|
||||
|
||||
leafMaterial.setBoolean("UseWind", false);
|
||||
leafMaterial.setTexture("WindNoise", noise);
|
||||
|
||||
leafMaterial.setFloat("AlphaDiscardThreshold", 0.5f);
|
||||
leafMaterial.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
||||
|
||||
// Hook it up to the atmospherics
|
||||
getState(SkyState.class).getAtmosphericParameters().applyGroundParameters(leafMaterial, true);
|
||||
|
||||
return leafMaterial;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* ${Id}
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.texture.Texture;
|
||||
import com.jme3.texture.Texture.WrapMode;
|
||||
import com.jme3.util.TangentBinormalGenerator;
|
||||
import com.simsilica.fx.sky.SkyState;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import com.simsilica.lemur.geom.MBox;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class GroundState extends BaseAppState {
|
||||
|
||||
private Material greenMaterial;
|
||||
private Material groundMaterial;
|
||||
private Geometry ground;
|
||||
|
||||
private boolean showGrass = false;
|
||||
private boolean useScattering = false;
|
||||
|
||||
public GroundState() {
|
||||
}
|
||||
|
||||
public void setUseScattering( boolean b ) {
|
||||
if( this.useScattering == b ) {
|
||||
return;
|
||||
}
|
||||
this.useScattering = b;
|
||||
resetScattering();
|
||||
}
|
||||
|
||||
public boolean getUseScattering() {
|
||||
return useScattering;
|
||||
}
|
||||
|
||||
public void setShowGrass( boolean b ) {
|
||||
this.showGrass = b;
|
||||
resetGrass();
|
||||
}
|
||||
|
||||
public boolean getShowGrass() {
|
||||
return showGrass;
|
||||
}
|
||||
|
||||
protected void resetGrass() {
|
||||
if( ground == null ) {
|
||||
return;
|
||||
}
|
||||
if( showGrass ) {
|
||||
ground.setMaterial(groundMaterial);
|
||||
} else {
|
||||
ground.setMaterial(greenMaterial);
|
||||
}
|
||||
}
|
||||
|
||||
protected void resetScattering() {
|
||||
if( groundMaterial != null ) {
|
||||
groundMaterial.setBoolean("UseScattering", useScattering);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
AssetManager assets = app.getAssetManager();
|
||||
|
||||
groundMaterial = GuiGlobals.getInstance().createMaterial(ColorRGBA.Green, true).getMaterial();
|
||||
|
||||
MBox b = new MBox(500, 0, 500, 50, 0, 50, MBox.TOP_MASK);
|
||||
TangentBinormalGenerator.generate(b);
|
||||
b.scaleTextureCoordinates(new Vector2f(1000, 1000));
|
||||
ground = new Geometry("Box", b);
|
||||
|
||||
greenMaterial = new Material(assets, "Common/MatDefs/Light/Lighting.j3md");
|
||||
greenMaterial.setColor("Diffuse", ColorRGBA.Green);
|
||||
greenMaterial.setColor("Ambient", ColorRGBA.Green);
|
||||
greenMaterial.setBoolean("UseMaterialColors", true);
|
||||
ground.setMaterial(greenMaterial);
|
||||
|
||||
|
||||
groundMaterial = new Material(assets, "MatDefs/MultiResolution.j3md");
|
||||
groundMaterial.setColor("Diffuse", ColorRGBA.White);
|
||||
groundMaterial.setColor("Specular", ColorRGBA.White);
|
||||
groundMaterial.setColor("Ambient", ColorRGBA.White);
|
||||
groundMaterial.setFloat("Shininess", 0);
|
||||
groundMaterial.setBoolean("UseMaterialColors", true);
|
||||
|
||||
// Hook up the scattering parameters
|
||||
getState(SkyState.class).getAtmosphericParameters().applyGroundParameters(groundMaterial, true);
|
||||
|
||||
Texture texture;
|
||||
|
||||
//texture = assets.loadTexture("Textures/test-pattern.png");
|
||||
texture = assets.loadTexture("Textures/grass.jpg");
|
||||
texture.setWrap(WrapMode.Repeat);
|
||||
groundMaterial.setTexture("DiffuseMap", texture);
|
||||
|
||||
texture = assets.loadTexture("Textures/grass-flat.jpg");
|
||||
texture.setWrap(WrapMode.Repeat);
|
||||
groundMaterial.setTexture("BackgroundDiffuseMap", texture);
|
||||
|
||||
texture = assets.loadTexture("Textures/brown-dirt-norm.jpg");
|
||||
//texture = assets.loadTexture("Textures/bark128-norm.jpg");
|
||||
texture.setWrap(WrapMode.Repeat);
|
||||
groundMaterial.setTexture("NormalMap", texture);
|
||||
|
||||
texture = assets.loadTexture("Textures/noise-x3-512.png");
|
||||
texture.setWrap(WrapMode.Repeat);
|
||||
groundMaterial.setTexture("NoiseMap", texture);
|
||||
|
||||
resetGrass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
Node rootNode = ((SimpleApplication)getApplication()).getRootNode();
|
||||
rootNode.attachChild(ground);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
ground.removeFromParent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* ${Id}
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.light.AmbientLight;
|
||||
import com.jme3.light.DirectionalLight;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Node;
|
||||
import com.simsilica.lemur.core.VersionedHolder;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class LegacyLightingState extends BaseAppState {
|
||||
|
||||
private VersionedHolder<Vector3f> lightDir = new VersionedHolder<Vector3f>();
|
||||
|
||||
private ColorRGBA sunColor;
|
||||
private DirectionalLight sun;
|
||||
private ColorRGBA ambientColor;
|
||||
private AmbientLight ambient;
|
||||
private float timeOfDay = FastMath.atan2(1, 0.3f) / FastMath.PI;
|
||||
private float inclination = FastMath.HALF_PI - FastMath.atan2(1, 0.4f);
|
||||
|
||||
private Node rootNode; // the one we added the lights to
|
||||
|
||||
public LegacyLightingState() {
|
||||
lightDir.setObject(new Vector3f(-0.2f, -1, -0.3f).normalizeLocal());
|
||||
this.sunColor = ColorRGBA.White.mult(2);
|
||||
this.ambientColor = new ColorRGBA(0.25f, 0.25f, 0.25f, 1);
|
||||
}
|
||||
|
||||
public DirectionalLight getSun() {
|
||||
return sun;
|
||||
}
|
||||
|
||||
public VersionedReference<Vector3f> getLightDirRef() {
|
||||
return lightDir.createReference();
|
||||
}
|
||||
|
||||
public void setTimeOfDay( float f ) {
|
||||
if( this.timeOfDay == f ) {
|
||||
return;
|
||||
}
|
||||
this.timeOfDay = f;
|
||||
resetLightDir();
|
||||
}
|
||||
|
||||
public float getTimeOfDay() {
|
||||
return timeOfDay;
|
||||
}
|
||||
|
||||
protected void resetLightDir() {
|
||||
float angle = timeOfDay * FastMath.PI;
|
||||
|
||||
Quaternion q1 = new Quaternion().fromAngles(0, 0, (angle - FastMath.HALF_PI));
|
||||
Quaternion q2 = new Quaternion().fromAngles(inclination, 0, 0);
|
||||
Vector3f dir = q2.mult(q1).mult(Vector3f.UNIT_Y.negate());
|
||||
lightDir.setObject(dir);
|
||||
sun.setDirection(lightDir.getObject());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
sun = new DirectionalLight();
|
||||
sun.setColor(sunColor);
|
||||
sun.setDirection(lightDir.getObject());
|
||||
|
||||
ambient = new AmbientLight();
|
||||
ambient.setColor(ambientColor);
|
||||
resetLightDir();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
rootNode = ((SimpleApplication)getApplication()).getRootNode();
|
||||
rootNode.addLight(sun);
|
||||
rootNode.addLight(ambient);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
rootNode.removeLight(sun);
|
||||
rootNode.removeLight(ambient);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* ${Id}
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial.CullHint;
|
||||
import com.jme3.scene.shape.Sphere;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class LegacySkyState extends BaseAppState {
|
||||
|
||||
private ColorRGBA skyColor;
|
||||
private ColorRGBA sunColor;
|
||||
private Geometry sky;
|
||||
private Geometry sun;
|
||||
private boolean showSky;
|
||||
|
||||
private VersionedReference<Vector3f> lightDir;
|
||||
|
||||
public LegacySkyState() {
|
||||
this.sunColor = new ColorRGBA(1, 1, 0.9f, 1);
|
||||
this.skyColor = new ColorRGBA(0.5f, 0.5f, 1f, 1);
|
||||
}
|
||||
|
||||
public void setShowSky( boolean b ) {
|
||||
this.showSky = b;
|
||||
resetShowSky();
|
||||
}
|
||||
|
||||
protected void resetShowSky() {
|
||||
if( sky == null ) {
|
||||
return;
|
||||
}
|
||||
if( showSky ) {
|
||||
sky.setCullHint(CullHint.Inherit);
|
||||
} else {
|
||||
sky.setCullHint(CullHint.Always);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getShowSky() {
|
||||
return sky.getCullHint() == CullHint.Inherit;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
lightDir = getState(LegacyLightingState.class).getLightDirRef();
|
||||
|
||||
|
||||
// Add a sun sphere
|
||||
Sphere orb = new Sphere(6, 12, 50);
|
||||
sun = new Geometry("Sun", orb);
|
||||
sun.setMaterial(GuiGlobals.getInstance().createMaterial(sunColor, false).getMaterial());
|
||||
sun.move(lightDir.get().mult(-900));
|
||||
|
||||
|
||||
// Add a sky sphere
|
||||
orb = new Sphere(6, 12, 800, true, true);
|
||||
sky = new Geometry("Sky", orb);
|
||||
sky.setMaterial(GuiGlobals.getInstance().createMaterial(skyColor, false).getMaterial());
|
||||
sky.setQueueBucket(Bucket.Sky);
|
||||
sky.setIgnoreTransform(true);
|
||||
|
||||
resetShowSky();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
Node rootNode = ((SimpleApplication)getApplication()).getRootNode();
|
||||
rootNode.attachChild(sun);
|
||||
rootNode.attachChild(sky);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( float tpf ) {
|
||||
if( lightDir.update() ) {
|
||||
sun.setLocalTranslation(lightDir.get().mult(-900));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
sun.removeFromParent();
|
||||
sky.removeFromParent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.input.KeyInput;
|
||||
import com.simsilica.lemur.input.FunctionId;
|
||||
import com.simsilica.lemur.input.InputMapper;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @version $Revision$
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class MainFunctions
|
||||
{
|
||||
public static final String GROUP = "Main";
|
||||
|
||||
public static final FunctionId F_TOGGLE_MOVEMENT = new FunctionId(GROUP, "Toggle Movement");
|
||||
public static final FunctionId F_HUD = new FunctionId(GROUP, "HUD Toggle");
|
||||
public static final FunctionId F_RECORD_VIDEO = new FunctionId(GROUP, "Record Video");
|
||||
|
||||
public static void initializeDefaultMappings( InputMapper inputMapper )
|
||||
{
|
||||
inputMapper.map( F_TOGGLE_MOVEMENT, KeyInput.KEY_SPACE );
|
||||
inputMapper.map( F_HUD, KeyInput.KEY_F3 );
|
||||
inputMapper.map( F_RECORD_VIDEO, KeyInput.KEY_F12 );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.input.KeyInput;
|
||||
import com.simsilica.lemur.input.Axis;
|
||||
import com.simsilica.lemur.input.Button;
|
||||
import com.simsilica.lemur.input.FunctionId;
|
||||
import com.simsilica.lemur.input.InputMapper;
|
||||
import com.simsilica.lemur.input.InputState;
|
||||
|
||||
|
||||
/**
|
||||
* The shared FunctionIds for camera movement input and
|
||||
* some default input mappings.
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class MovementFunctions {
|
||||
|
||||
/**
|
||||
* Camera movement functions will all be in the "Movement" group
|
||||
* to make it easy to toggle camera input on and off.
|
||||
*/
|
||||
public static final String GROUP_MOVEMENT = "Movement";
|
||||
|
||||
public static final FunctionId F_Y_LOOK = new FunctionId(GROUP_MOVEMENT, "Y Look");
|
||||
public static final FunctionId F_X_LOOK = new FunctionId(GROUP_MOVEMENT, "X Look");
|
||||
|
||||
public static final FunctionId F_MOVE = new FunctionId(GROUP_MOVEMENT, "Move");
|
||||
public static final FunctionId F_STRAFE = new FunctionId(GROUP_MOVEMENT, "Strafe");
|
||||
|
||||
public static final FunctionId F_ELEVATE = new FunctionId(GROUP_MOVEMENT, "Elevate");
|
||||
|
||||
public static final FunctionId F_RUN = new FunctionId(GROUP_MOVEMENT, "Run");
|
||||
public static final FunctionId F_SUPER_RUN = new FunctionId(GROUP_MOVEMENT, "Super Run");
|
||||
|
||||
/**
|
||||
* We capture some input mappings in case they need
|
||||
* to be scaled or flipped later. Mouse and joystick input are
|
||||
* often configured with sensitivity settings and this is one
|
||||
* way to do that built into InputMapper.
|
||||
*/
|
||||
public static InputMapper.Mapping MOUSE_X_LOOK;
|
||||
public static InputMapper.Mapping MOUSE_Y_LOOK;
|
||||
public static InputMapper.Mapping JOY_X_LOOK;
|
||||
public static InputMapper.Mapping JOY_Y_LOOK;
|
||||
|
||||
public static void initializeDefaultMappings( InputMapper inputMapper )
|
||||
{
|
||||
// The joystick Y axes are backwards on game pads... forward
|
||||
// is negative. So we'll flip it over in the mapping.
|
||||
inputMapper.map( F_MOVE, InputState.Negative, Axis.JOYSTICK_LEFT_Y );
|
||||
|
||||
// Here a similar approach is used to map the W and S keys with
|
||||
// 'S' being negative. In this way, W and S now act like a joystick
|
||||
// axis.
|
||||
inputMapper.map( F_MOVE, KeyInput.KEY_W );
|
||||
inputMapper.map( F_MOVE, InputState.Negative, KeyInput.KEY_S );
|
||||
|
||||
// Strafing is setup similar to move.
|
||||
inputMapper.map( F_STRAFE, Axis.JOYSTICK_LEFT_X );
|
||||
inputMapper.map( F_STRAFE, KeyInput.KEY_D );
|
||||
inputMapper.map( F_STRAFE, InputState.Negative, KeyInput.KEY_A );
|
||||
|
||||
// Elevation only has key mappings but we still treat it like
|
||||
// one "axis".
|
||||
inputMapper.map( F_ELEVATE, KeyInput.KEY_Q );
|
||||
inputMapper.map( F_ELEVATE, InputState.Negative, KeyInput.KEY_Z );
|
||||
|
||||
// For the mouse and joystick mappings, we remember the mapping object
|
||||
// in case we want to flip it or adjust its sensitivity later. This
|
||||
// is better than trying to do it in the function handler because it's
|
||||
// quite often input specific. The user may want to flip the joystick
|
||||
// but not the mouse or adjust sensitivity on one and not the other.
|
||||
MOUSE_X_LOOK = inputMapper.map( F_X_LOOK, Axis.MOUSE_X );
|
||||
JOY_X_LOOK = inputMapper.map( F_X_LOOK, Axis.JOYSTICK_RIGHT_X );
|
||||
inputMapper.map( F_X_LOOK, KeyInput.KEY_RIGHT );
|
||||
inputMapper.map( F_X_LOOK, InputState.Negative, KeyInput.KEY_LEFT );
|
||||
|
||||
MOUSE_Y_LOOK = inputMapper.map( F_Y_LOOK, Axis.MOUSE_Y );
|
||||
JOY_Y_LOOK = inputMapper.map( F_Y_LOOK, Axis.JOYSTICK_RIGHT_Y );
|
||||
inputMapper.map( F_Y_LOOK, KeyInput.KEY_UP );
|
||||
inputMapper.map( F_Y_LOOK, InputState.Negative, KeyInput.KEY_DOWN );
|
||||
|
||||
// Here we give run its own function. Note that it was also possible
|
||||
// to treat running like an axis used instead of MOVE by combining
|
||||
// keys. For example:
|
||||
// map( F_RUN, KeyInput.KEY_LSHIFT, KeyInput.KEY_W )
|
||||
// Another approach would have been to set the 'scale' of the non-run
|
||||
// mapping to 0.5 or something and simply map SHIFT+W without the
|
||||
// scaling. That would have done two things:
|
||||
// 1) the camera code simply needs to look for MOVE and not worry
|
||||
// about a separate 'run mode' because walking will be half-speed
|
||||
// move anyway.
|
||||
// 2) the joystick would always be running which means the player simply
|
||||
// runs by using full stick.
|
||||
//
|
||||
// Instead, I've opted for a run mode toggle. Mostly because it demonstrates
|
||||
// state mapping where as all of the other inputs so far are going to
|
||||
// be treated as analog. That means our joystick needs a run button also.
|
||||
inputMapper.map( F_RUN, KeyInput.KEY_LSHIFT );
|
||||
inputMapper.map( F_RUN, Button.JOYSTICK_RIGHT1 );
|
||||
|
||||
inputMapper.map( F_SUPER_RUN, KeyInput.KEY_LSHIFT, KeyInput.KEY_LCONTROL );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.math.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public interface MovementHandler
|
||||
{
|
||||
public void setLocation( Vector3f loc );
|
||||
public Vector3f getLocation();
|
||||
|
||||
public void setFacing( Quaternion facing );
|
||||
public Quaternion getFacing();
|
||||
}
|
||||
@@ -0,0 +1,304 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.core.VersionedHolder;
|
||||
import com.simsilica.lemur.core.VersionedObject;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import com.simsilica.lemur.input.AnalogFunctionListener;
|
||||
import com.simsilica.lemur.input.FunctionId;
|
||||
import com.simsilica.lemur.input.InputMapper;
|
||||
import com.simsilica.lemur.input.InputState;
|
||||
import com.simsilica.lemur.input.StateFunctionListener;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author PSpeed
|
||||
*/
|
||||
public class MovementState extends BaseAppState
|
||||
implements AnalogFunctionListener, StateFunctionListener {
|
||||
|
||||
private InputMapper inputMapper;
|
||||
private MovementHandler mover;
|
||||
private double turnSpeed = 2.5; // one half complete revolution in 2.5 seconds
|
||||
private double yaw = FastMath.PI;
|
||||
private double pitch;
|
||||
private double maxPitch = FastMath.HALF_PI;
|
||||
private double minPitch = -FastMath.HALF_PI;
|
||||
private Quaternion facing = new Quaternion().fromAngles((float)pitch, (float)yaw, 0);
|
||||
private double forward;
|
||||
private double side;
|
||||
private double elevation;
|
||||
private double speed = 3.0;
|
||||
private VersionedHolder<Vector3f> worldPos = new VersionedHolder<Vector3f>(new Vector3f());
|
||||
|
||||
public MovementState() {
|
||||
}
|
||||
|
||||
public void toggleEnabled() {
|
||||
setEnabled(!isEnabled());
|
||||
}
|
||||
|
||||
public VersionedObject<Vector3f> getWorldPosition() {
|
||||
return worldPos;
|
||||
}
|
||||
|
||||
public void setMovementHandler( MovementHandler mover ) {
|
||||
this.mover = mover;
|
||||
updateFacing();
|
||||
}
|
||||
|
||||
public MovementHandler getMovementHandler() {
|
||||
return mover;
|
||||
}
|
||||
|
||||
public void setPitch( double pitch ) {
|
||||
this.pitch = pitch;
|
||||
updateFacing();
|
||||
}
|
||||
|
||||
public double getPitch() {
|
||||
return pitch;
|
||||
}
|
||||
|
||||
public void setYaw( double yaw ) {
|
||||
this.yaw = yaw;
|
||||
updateFacing();
|
||||
}
|
||||
|
||||
public double getYaw() {
|
||||
return yaw;
|
||||
}
|
||||
|
||||
public void setRotation( Quaternion rotation ) {
|
||||
// Do our best
|
||||
float[] angle = rotation.toAngles(null);
|
||||
this.pitch = angle[0];
|
||||
this.yaw = angle[1];
|
||||
updateFacing();
|
||||
}
|
||||
|
||||
public Quaternion getRotation() {
|
||||
return mover.getFacing();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
if( this.mover == null ) {
|
||||
this.mover = new CameraMovementHandler(app.getCamera());
|
||||
}
|
||||
|
||||
if( inputMapper == null )
|
||||
inputMapper = GuiGlobals.getInstance().getInputMapper();
|
||||
|
||||
inputMapper.addDelegate( MainFunctions.F_TOGGLE_MOVEMENT, this, "toggleEnabled" );
|
||||
|
||||
// Most of the movement functions are treated as analog.
|
||||
inputMapper.addAnalogListener(this,
|
||||
MovementFunctions.F_Y_LOOK,
|
||||
MovementFunctions.F_X_LOOK,
|
||||
MovementFunctions.F_MOVE,
|
||||
MovementFunctions.F_ELEVATE,
|
||||
MovementFunctions.F_STRAFE);
|
||||
|
||||
// Only run mode is treated as a 'state' or a trinary value.
|
||||
// (Positive, Off, Negative) and in this case we only care about
|
||||
// Positive and Off. See MovementFunctions for a description
|
||||
// of alternate ways this could have been done.
|
||||
inputMapper.addStateListener(this,
|
||||
MovementFunctions.F_RUN,
|
||||
MovementFunctions.F_SUPER_RUN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup(Application app) {
|
||||
|
||||
inputMapper.removeDelegate( MainFunctions.F_TOGGLE_MOVEMENT, this, "toggleEnabled" );
|
||||
|
||||
inputMapper.removeAnalogListener( this,
|
||||
MovementFunctions.F_Y_LOOK,
|
||||
MovementFunctions.F_X_LOOK,
|
||||
MovementFunctions.F_MOVE,
|
||||
MovementFunctions.F_ELEVATE,
|
||||
MovementFunctions.F_STRAFE);
|
||||
inputMapper.removeStateListener( this,
|
||||
MovementFunctions.F_RUN,
|
||||
MovementFunctions.F_SUPER_RUN);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
// Make sure our input group is enabled
|
||||
inputMapper.activateGroup( MovementFunctions.GROUP_MOVEMENT );
|
||||
|
||||
// And kill the cursor
|
||||
GuiGlobals.getInstance().setCursorEventsEnabled(false);
|
||||
|
||||
// A 'bug' in Lemur causes it to miss turning the cursor off if
|
||||
// we are enabled before the MouseAppState is initialized.
|
||||
getApplication().getInputManager().setCursorVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
inputMapper.deactivateGroup( MovementFunctions.GROUP_MOVEMENT );
|
||||
GuiGlobals.getInstance().setCursorEventsEnabled(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( float tpf ) {
|
||||
|
||||
// 'integrate' camera position based on the current move, strafe,
|
||||
// and elevation speeds.
|
||||
if( forward != 0 || side != 0 || elevation != 0 ) {
|
||||
Vector3f loc = mover.getLocation();
|
||||
|
||||
Quaternion rot = mover.getFacing();
|
||||
Vector3f move = rot.mult(Vector3f.UNIT_Z).multLocal((float)(forward * speed * tpf));
|
||||
Vector3f strafe = rot.mult(Vector3f.UNIT_X).multLocal((float)(side * speed * tpf));
|
||||
|
||||
// Note: this camera moves 'elevation' along the camera's current up
|
||||
// vector because I find it more intuitive in free flight.
|
||||
Vector3f elev = rot.mult(Vector3f.UNIT_Y).multLocal((float)(elevation * speed * tpf));
|
||||
|
||||
loc = loc.add(move).add(strafe).add(elev);
|
||||
mover.setLocation(loc);
|
||||
worldPos.setObject(loc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the StateFunctionListener interface.
|
||||
*/
|
||||
@Override
|
||||
public void valueChanged( FunctionId func, InputState value, double tpf ) {
|
||||
|
||||
// Change the speed based on the current run mode
|
||||
// Another option would have been to use the value
|
||||
// directly:
|
||||
// speed = 3 + value.asNumber() * 5
|
||||
//...but I felt it was slightly less clear here.
|
||||
boolean b = value == InputState.Positive;
|
||||
if( func == MovementFunctions.F_RUN ) {
|
||||
if( b ) {
|
||||
speed = 10;
|
||||
} else {
|
||||
speed = 3;
|
||||
}
|
||||
} else if( func == MovementFunctions.F_SUPER_RUN ) {
|
||||
if( b ) {
|
||||
speed = 20;
|
||||
} else {
|
||||
speed = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the AnalogFunctionListener interface.
|
||||
*/
|
||||
@Override
|
||||
public void valueActive( FunctionId func, double value, double tpf ) {
|
||||
|
||||
// Setup rotations and movements speeds based on current
|
||||
// axes states.
|
||||
if( func == MovementFunctions.F_Y_LOOK ) {
|
||||
pitch += -value * tpf * turnSpeed;
|
||||
if( pitch < minPitch )
|
||||
pitch = minPitch;
|
||||
if( pitch > maxPitch )
|
||||
pitch = maxPitch;
|
||||
} else if( func == MovementFunctions.F_X_LOOK ) {
|
||||
double yawDelta = -value * tpf * turnSpeed;
|
||||
yaw += yawDelta;
|
||||
if( yaw < 0 )
|
||||
yaw += Math.PI * 2;
|
||||
if( yaw > Math.PI * 2 )
|
||||
yaw -= Math.PI * 2;
|
||||
} else if( func == MovementFunctions.F_MOVE ) {
|
||||
this.forward = value;
|
||||
return;
|
||||
} else if( func == MovementFunctions.F_STRAFE ) {
|
||||
this.side = -value;
|
||||
return;
|
||||
} else if( func == MovementFunctions.F_ELEVATE ) {
|
||||
this.elevation = value;
|
||||
return;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
updateFacing();
|
||||
}
|
||||
|
||||
protected void updateFacing() {
|
||||
facing.fromAngles( (float)pitch, (float)yaw, 0 );
|
||||
mover.setFacing(facing);
|
||||
}
|
||||
|
||||
public static class CameraMovementHandler implements MovementHandler {
|
||||
private Camera camera;
|
||||
|
||||
public CameraMovementHandler( Camera camera ) {
|
||||
this.camera = camera;
|
||||
}
|
||||
|
||||
public void setLocation( Vector3f loc ) {
|
||||
camera.setLocation(loc);
|
||||
}
|
||||
|
||||
public Vector3f getLocation() {
|
||||
return camera.getLocation();
|
||||
}
|
||||
|
||||
public void setFacing( Quaternion facing ) {
|
||||
camera.setRotation(facing);
|
||||
}
|
||||
|
||||
public Quaternion getFacing() {
|
||||
return camera.getRotation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* ${Id}
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.asset.AssetManager;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.post.FilterPostProcessor;
|
||||
import com.jme3.shadow.DirectionalLightShadowFilter;
|
||||
import com.jme3.system.AppSettings;
|
||||
import com.simsilica.fx.LightingState;
|
||||
import com.simsilica.lemur.Checkbox;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import com.simsilica.lemur.props.PropertyPanel;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class PostProcessorState extends BaseAppState {
|
||||
|
||||
private FilterPostProcessor fpp;
|
||||
private DirectionalLightShadowFilter shadows1;
|
||||
private DropShadowFilter shadows2;
|
||||
private VersionedReference<Vector3f> lightDir;
|
||||
private float shadowStrength = 0.3f;
|
||||
|
||||
private Checkbox shadowToggle;
|
||||
private Checkbox dropShadowToggle;
|
||||
|
||||
public PostProcessorState() {
|
||||
}
|
||||
|
||||
public void setEnableShadows( boolean b ) {
|
||||
shadows1.setEnabled(b);
|
||||
if( b && shadows2.isEnabled() ) {
|
||||
shadows2.setEnabled(false);
|
||||
dropShadowToggle.setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEnableDropShadows( boolean b ) {
|
||||
shadows2.setEnabled(b);
|
||||
if( b && shadows1.isEnabled() ) {
|
||||
shadows1.setEnabled(false);
|
||||
shadowToggle.setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setShadowStrength( float f ) {
|
||||
if( this.shadowStrength == f ) {
|
||||
return;
|
||||
}
|
||||
this.shadowStrength = f;
|
||||
resetShadowStrength();
|
||||
}
|
||||
|
||||
public float getShadowStrength() {
|
||||
return shadowStrength;
|
||||
}
|
||||
|
||||
protected void resetShadowStrength() {
|
||||
shadows1.setShadowIntensity(shadowStrength);
|
||||
shadows2.setShadowIntensity(shadowStrength);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
lightDir = getState(LightingState.class).getLightDirRef();
|
||||
|
||||
AssetManager assets = app.getAssetManager();
|
||||
|
||||
fpp = new FilterPostProcessor(assets);
|
||||
AppSettings settings = app.getContext().getSettings();
|
||||
if( settings.getSamples() != 0 )
|
||||
{
|
||||
fpp.setNumSamples(settings.getSamples());
|
||||
}
|
||||
|
||||
shadows2 = new DropShadowFilter();
|
||||
shadows2.setEnabled(false);
|
||||
fpp.addFilter(shadows2);
|
||||
|
||||
shadows1 = new DirectionalLightShadowFilter(assets, 4096, 4);
|
||||
shadows1.setShadowIntensity(0.3f);
|
||||
shadows1.setLight(getState(LightingState.class).getSun());
|
||||
shadows1.setEnabled(false);
|
||||
fpp.addFilter(shadows1);
|
||||
|
||||
// Go ahead and add some UI stuff here... normally I'd
|
||||
// put it in another state but it doesn't seem worth it.
|
||||
TreeOptionsState options = getState(TreeOptionsState.class);
|
||||
shadowToggle = options.addOptionToggle("Shadows", this, "setEnableShadows");
|
||||
dropShadowToggle = options.addOptionToggle("Drop Shadows", this, "setEnableDropShadows");
|
||||
|
||||
|
||||
// Add some parameters to the main options window
|
||||
PropertyPanel properties = new PropertyPanel("glass");
|
||||
getState(TreeOptionsState.class).getViewSettings().addChild(properties);
|
||||
|
||||
properties.addFloatProperty("Time of day (*)", getState(LightingState.class),
|
||||
"timeOfDay", 0, 1, 0.05f);
|
||||
properties.addFloatProperty("Shadow Strength (*)", this, "shadowStrength", 0, 1, 0.05f);
|
||||
|
||||
resetShadowStrength();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
getApplication().getViewPort().addProcessor(fpp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update( float tpf ) {
|
||||
if( lightDir.update() ) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
getApplication().getViewPort().removeProcessor(fpp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,510 @@
|
||||
/*
|
||||
* ${Id}
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.math.ColorRGBA;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial.CullHint;
|
||||
import com.jme3.scene.VertexBuffer;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import com.simsilica.arboreal.mesh.BillboardedLeavesMeshGenerator;
|
||||
import com.simsilica.arboreal.mesh.FlatPolyTreeMeshGenerator;
|
||||
import com.simsilica.arboreal.mesh.LodSwitchControl;
|
||||
import com.simsilica.arboreal.mesh.SkinnedTreeMeshGenerator;
|
||||
import com.simsilica.arboreal.mesh.Vertex;
|
||||
import com.simsilica.builder.Builder;
|
||||
import com.simsilica.builder.BuilderReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class TreeBuilderReference implements BuilderReference
|
||||
{
|
||||
static Logger log = LoggerFactory.getLogger(TreeBuilderReference.class);
|
||||
|
||||
private int priority;
|
||||
|
||||
private int seed;
|
||||
private Material treeMaterial;
|
||||
private Material wireMaterial;
|
||||
private Material leafMaterial;
|
||||
private Material flatMaterial;
|
||||
private Material flatWireMaterial;
|
||||
private Material impostorMaterial;
|
||||
private Material impostorWireMaterial;
|
||||
private TreeParameters treeParameters;
|
||||
|
||||
private Node treeNode;
|
||||
|
||||
private LevelGeometry[] lods;
|
||||
private volatile LevelGeometry[] newLods;
|
||||
|
||||
private boolean showWire;
|
||||
|
||||
private AtomicInteger needsUpdate = new AtomicInteger(1);
|
||||
|
||||
// For debugging
|
||||
private volatile boolean check = false;
|
||||
|
||||
public TreeBuilderReference( TreeParameters treeParameters,
|
||||
Material treeMaterial,
|
||||
Material wireMaterial,
|
||||
Material leafMaterial,
|
||||
Material flatMaterial,
|
||||
Material impostorMaterial ) {
|
||||
this.treeParameters = treeParameters;
|
||||
this.treeMaterial = treeMaterial;
|
||||
this.wireMaterial = wireMaterial;
|
||||
this.leafMaterial = leafMaterial;
|
||||
this.flatMaterial = flatMaterial;
|
||||
this.impostorMaterial = impostorMaterial;
|
||||
|
||||
lods = new LevelGeometry[treeParameters.getLodCount()];
|
||||
treeNode = new Node("Tree");
|
||||
treeNode.addControl(new LodSwitchControl());
|
||||
|
||||
// We'll derive the flat wire material from the flat material
|
||||
flatWireMaterial = flatMaterial.clone();
|
||||
flatWireMaterial.clearParam("DiffuseMap");
|
||||
flatWireMaterial.setColor("Diffuse", ColorRGBA.Yellow.mult(10));
|
||||
flatWireMaterial.setColor("Ambient", ColorRGBA.Yellow.mult(10));
|
||||
flatWireMaterial.getAdditionalRenderState().setWireframe(true);
|
||||
|
||||
impostorWireMaterial = impostorMaterial.clone();
|
||||
impostorWireMaterial.clearParam("DiffuseMap");
|
||||
impostorWireMaterial.setColor("Diffuse", ColorRGBA.Yellow.mult(10));
|
||||
impostorWireMaterial.setColor("Ambient", ColorRGBA.Yellow.mult(10));
|
||||
impostorWireMaterial.getAdditionalRenderState().setWireframe(true);
|
||||
}
|
||||
|
||||
public void setSeed( int seed ) {
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
public void setWireFrame( boolean b ) {
|
||||
if( showWire == b ) {
|
||||
return;
|
||||
}
|
||||
this.showWire = b;
|
||||
for( LevelGeometry level : lods ) {
|
||||
if( level == null ) {
|
||||
continue;
|
||||
}
|
||||
if( showWire ) {
|
||||
level.wireGeom.setCullHint(CullHint.Inherit);
|
||||
} else {
|
||||
level.wireGeom.setCullHint(CullHint.Always);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void markChanged() {
|
||||
needsUpdate.incrementAndGet();
|
||||
}
|
||||
|
||||
public Node getTreeNode() {
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
public int getVertexCount( int lod ) {
|
||||
LevelGeometry level = lods[lod];
|
||||
if( level == null ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
if( level.treeGeom != null ) {
|
||||
Mesh mesh = level.treeGeom.getMesh();
|
||||
result += mesh.getVertexCount();
|
||||
}
|
||||
if( level.leafGeom != null ) {
|
||||
Mesh mesh = level.leafGeom.getMesh();
|
||||
result += mesh.getVertexCount();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getTriangleCount( int lod ) {
|
||||
LevelGeometry level = lods[lod];
|
||||
if( level == null ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
if( level.treeGeom != null ) {
|
||||
Mesh mesh = level.treeGeom.getMesh();
|
||||
result += mesh.getTriangleCount();
|
||||
}
|
||||
if( level.leafGeom != null ) {
|
||||
Mesh mesh = level.leafGeom.getMesh();
|
||||
result += mesh.getTriangleCount();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getVertexCount() {
|
||||
return getVertexCount(0);
|
||||
}
|
||||
|
||||
public int getTriangleCount() {
|
||||
return getTriangleCount(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return priority;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build() {
|
||||
if( needsUpdate.get() == 0 ) {
|
||||
return;
|
||||
}
|
||||
needsUpdate.set(0);
|
||||
|
||||
check = true;
|
||||
try {
|
||||
log.trace("******* rebuilding tree ********");
|
||||
regenerateTree();
|
||||
log.trace("******* tree built ********" );
|
||||
} finally {
|
||||
check = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
if( newLods == null ) {
|
||||
// Nothing was built
|
||||
return;
|
||||
}
|
||||
|
||||
if( check ) {
|
||||
log.error( "Ships have passed in the night 1." );
|
||||
}
|
||||
|
||||
log.trace("******* applying tree ********" );
|
||||
|
||||
LodSwitchControl lodControl = treeNode.getControl(LodSwitchControl.class);
|
||||
lodControl.clearLevels();
|
||||
|
||||
// Release the old ones if we had them.
|
||||
for( LevelGeometry g : lods ) {
|
||||
if( g != null ) {
|
||||
g.release();
|
||||
}
|
||||
}
|
||||
|
||||
// Add in the new ones
|
||||
for( int i = 0; i < lods.length; i++ ) {
|
||||
lods[i] = newLods[i];
|
||||
lods[i].attach(lodControl);
|
||||
if( !showWire && lods[i] != null && lods[i].wireGeom != null ) {
|
||||
lods[i].wireGeom.setCullHint(CullHint.Always);
|
||||
}
|
||||
}
|
||||
newLods = null;
|
||||
|
||||
log.trace("******* tree applied ********" );
|
||||
if( check ) {
|
||||
log.error( "Ships have passed in the night 2." );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
for( LevelGeometry g : lods ) {
|
||||
if( g != null ) {
|
||||
g.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void releaseGeometry( Geometry geom ) {
|
||||
if( geom == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
geom.removeFromParent();
|
||||
|
||||
Mesh mesh = geom.getMesh();
|
||||
releaseMesh(mesh);
|
||||
}
|
||||
|
||||
protected void releaseMesh( Mesh mesh ) {
|
||||
// Delete the old buffers
|
||||
for( VertexBuffer vb : mesh.getBufferList() ) {
|
||||
if( log.isTraceEnabled() ) {
|
||||
log.trace("--destroying buffer:" + vb);
|
||||
}
|
||||
BufferUtils.destroyDirectBuffer( vb.getData() );
|
||||
}
|
||||
}
|
||||
|
||||
protected void regenerateTree() {
|
||||
|
||||
LevelGeometry[] levels = new LevelGeometry[treeParameters.getLodCount()];
|
||||
|
||||
TreeGenerator treeGen = new TreeGenerator();
|
||||
Tree tree = treeGen.generateTree(seed, treeParameters);
|
||||
|
||||
BoundingBox trunkBounds = null;
|
||||
BoundingBox leafBounds = null;
|
||||
|
||||
List<Vertex> baseTips = null;
|
||||
|
||||
for( int i = 0; i < levels.length; i++ ) {
|
||||
|
||||
LevelOfDetailParameters lodParms = treeParameters.getLod(i);
|
||||
LevelGeometry level = new LevelGeometry(lodParms.distance);
|
||||
levels[i] = level;
|
||||
|
||||
Mesh treeMesh;
|
||||
List<Vertex> tips = null;
|
||||
boolean generateLeaves = false;
|
||||
|
||||
switch( lodParms.reduction ) {
|
||||
case Normal:
|
||||
SkinnedTreeMeshGenerator meshGen = new SkinnedTreeMeshGenerator();
|
||||
|
||||
if( baseTips == null ) {
|
||||
baseTips = tips = new ArrayList<Vertex>();
|
||||
}
|
||||
treeMesh = meshGen.generateMesh(tree,
|
||||
treeParameters.getLod(i),
|
||||
treeParameters.getYOffset(),
|
||||
treeParameters.getTextureURepeat(),
|
||||
treeParameters.getTextureVScale(),
|
||||
tips);
|
||||
trunkBounds = (BoundingBox)treeMesh.getBound();
|
||||
|
||||
level.treeGeom = new Geometry("tree:" + lodParms.reduction, treeMesh);
|
||||
level.treeGeom.setMaterial(treeMaterial);
|
||||
level.treeGeom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
level.treeGeom.setLocalTranslation(0, treeParameters.getRootHeight(), 0);
|
||||
|
||||
level.wireGeom = new Geometry("wire:" + lodParms.reduction, treeMesh);
|
||||
level.wireGeom.setMaterial(wireMaterial);
|
||||
level.wireGeom.setLocalTranslation(0, treeParameters.getRootHeight(), 0);
|
||||
|
||||
generateLeaves = true;
|
||||
break;
|
||||
case FlatPoly:
|
||||
FlatPolyTreeMeshGenerator polyGen = new FlatPolyTreeMeshGenerator();
|
||||
if( baseTips == null ) {
|
||||
baseTips = tips = new ArrayList<Vertex>();
|
||||
}
|
||||
treeMesh = polyGen.generateMesh(tree,
|
||||
treeParameters.getLod(i),
|
||||
treeParameters.getYOffset(),
|
||||
treeParameters.getTextureURepeat(),
|
||||
treeParameters.getTextureVScale(),
|
||||
tips);
|
||||
|
||||
level.treeGeom = new Geometry("tree:" + lodParms.reduction, treeMesh);
|
||||
level.treeGeom.setMaterial(flatMaterial);
|
||||
level.treeGeom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
level.treeGeom.setLocalTranslation(0, treeParameters.getRootHeight(), 0);
|
||||
|
||||
level.wireGeom = new Geometry("wire:" + lodParms.reduction, treeMesh);
|
||||
level.wireGeom.setMaterial(flatWireMaterial);
|
||||
level.wireGeom.setLocalTranslation(0, treeParameters.getRootHeight(), 0);
|
||||
|
||||
generateLeaves = true;
|
||||
break;
|
||||
case Impostor:
|
||||
|
||||
if( trunkBounds == null ) {
|
||||
// Generate the mesh just to throw it away
|
||||
meshGen = new SkinnedTreeMeshGenerator();
|
||||
|
||||
if( baseTips == null ) {
|
||||
baseTips = tips = new ArrayList<Vertex>();
|
||||
}
|
||||
treeMesh = meshGen.generateMesh(tree,
|
||||
treeParameters.getLod(0),
|
||||
treeParameters.getYOffset(),
|
||||
treeParameters.getTextureURepeat(),
|
||||
treeParameters.getTextureVScale(),
|
||||
tips);
|
||||
trunkBounds = (BoundingBox)treeMesh.getBound();
|
||||
releaseMesh(treeMesh);
|
||||
}
|
||||
BoundingBox impostorBounds = (BoundingBox)trunkBounds.clone();
|
||||
|
||||
if( leafBounds == null && treeParameters.getGenerateLeaves() ) {
|
||||
BillboardedLeavesMeshGenerator leafGen = new BillboardedLeavesMeshGenerator();
|
||||
Mesh leafMesh = leafGen.generateMesh(baseTips, treeParameters.getLeafScale());
|
||||
leafBounds = (BoundingBox)leafMesh.getBound();
|
||||
releaseMesh(leafMesh);
|
||||
} else if( treeParameters.getGenerateLeaves() ) {
|
||||
impostorBounds.mergeLocal(leafBounds);
|
||||
}
|
||||
|
||||
float rootHeight = treeParameters.getRootHeight();
|
||||
Vector3f min = trunkBounds.getMin(null);
|
||||
Vector3f max = trunkBounds.getMax(null);
|
||||
if( leafBounds != null ) {
|
||||
min.minLocal(leafBounds.getMin(null));
|
||||
max.maxLocal(leafBounds.getMax(null));
|
||||
}
|
||||
//float radius = (max.y - min.y) * 0.5f;
|
||||
|
||||
float xSize = Math.max(Math.abs(min.x), Math.abs(max.x));
|
||||
float ySize = max.y - min.y;
|
||||
float zSize = Math.max(Math.abs(min.z), Math.abs(max.z));
|
||||
|
||||
float size = ySize * 0.5f;
|
||||
size = Math.max(size, xSize);
|
||||
size = Math.max(size, zSize);
|
||||
float radius = size;
|
||||
|
||||
// Just do it here raw for now
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.setBuffer(Type.Position, 3, new float[] {
|
||||
0, min.y + rootHeight, 0,
|
||||
0, min.y + rootHeight, 0,
|
||||
0, min.y + (size*2) + rootHeight, 0,
|
||||
0, min.y + (size*2) + rootHeight, 0
|
||||
//0, max.y + rootHeight, 0,
|
||||
//0, max.y + rootHeight, 0
|
||||
});
|
||||
mesh.setBuffer(Type.Size, 1, new float[] {
|
||||
-radius,
|
||||
radius,
|
||||
-radius,
|
||||
radius
|
||||
});
|
||||
mesh.setBuffer(Type.TexCoord, 2, new float[] {
|
||||
0, 0,
|
||||
1, 0,
|
||||
0, 1f,
|
||||
1, 1f
|
||||
});
|
||||
mesh.setBuffer(Type.Index, 3, new short[] {
|
||||
0, 1, 3,
|
||||
0, 3, 2
|
||||
});
|
||||
//mesh.updateBound();
|
||||
|
||||
// Give the mesh the same bound that the real tree would have
|
||||
// had.
|
||||
impostorBounds.getCenter().addLocal(0, treeParameters.getRootHeight(), 0);
|
||||
mesh.setBound(impostorBounds);
|
||||
|
||||
level.treeGeom = new Geometry("tree:" + lodParms.reduction, mesh);
|
||||
level.treeGeom.setMaterial(impostorMaterial);
|
||||
level.treeGeom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
level.treeGeom.setLocalTranslation(0, 0, 0);
|
||||
level.treeGeom.setQueueBucket(Bucket.Transparent);
|
||||
|
||||
level.wireGeom = new Geometry("wire:" + lodParms.reduction, mesh);
|
||||
level.wireGeom.setMaterial(impostorWireMaterial);
|
||||
level.wireGeom.setLocalTranslation(0, 0, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if( generateLeaves && treeParameters.getGenerateLeaves() && baseTips != null ) {
|
||||
BillboardedLeavesMeshGenerator leafGen = new BillboardedLeavesMeshGenerator();
|
||||
Mesh leafMesh = leafGen.generateMesh(baseTips, treeParameters.getLeafScale());
|
||||
leafBounds = (BoundingBox)leafMesh.getBound();
|
||||
level.leafGeom = new Geometry("leaves:" + lodParms.reduction, leafMesh);
|
||||
level.leafGeom.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
|
||||
level.leafGeom.setQueueBucket(Bucket.Transparent);
|
||||
level.leafGeom.setMaterial(leafMaterial);
|
||||
|
||||
level.leafGeom.setLocalTranslation(0, treeParameters.getRootHeight(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
newLods = levels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates all of the tree geometry for a
|
||||
* particular level of detail.
|
||||
*/
|
||||
private class LevelGeometry {
|
||||
|
||||
float distance;
|
||||
Node levelNode;
|
||||
Geometry treeGeom;
|
||||
Geometry wireGeom;
|
||||
Geometry leafGeom;
|
||||
|
||||
public LevelGeometry( float distance ) {
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
public void attach( LodSwitchControl control ) {
|
||||
levelNode = new Node("level:" + distance);
|
||||
if( treeGeom != null ) {
|
||||
levelNode.attachChild(treeGeom);
|
||||
levelNode.attachChild(wireGeom);
|
||||
}
|
||||
if( leafGeom != null ) {
|
||||
levelNode.attachChild(leafGeom);
|
||||
}
|
||||
control.addLevel(distance, levelNode);
|
||||
}
|
||||
|
||||
public void release() {
|
||||
levelNode.removeFromParent();
|
||||
releaseGeometry(treeGeom);
|
||||
releaseGeometry(wireGeom);
|
||||
releaseGeometry(leafGeom);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
159
simarboreal/src/main/java/com/simsilica/arboreal/TreeEditor.java
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.simsilica.builder.BuilderState;
|
||||
import com.jme3.app.DebugKeysAppState;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.jme3.app.StatsAppState;
|
||||
import com.jme3.app.state.ScreenshotAppState;
|
||||
import com.jme3.app.state.VideoRecorderAppState;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.system.AppSettings;
|
||||
import com.simsilica.fx.LightingState;
|
||||
import com.simsilica.fx.sky.SkyState;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.input.InputMapper;
|
||||
import com.simsilica.lemur.style.BaseStyles;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Main class for the TreeEditor.
|
||||
*
|
||||
* @author PSpeed
|
||||
*/
|
||||
public class TreeEditor extends SimpleApplication {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(TreeEditor.class);
|
||||
|
||||
public static final String GLASS_STYLES = "/com/simsilica/arboreal/ui/glass-styles.groovy";
|
||||
|
||||
public static void main( String... args ) {
|
||||
TreeEditor main = new TreeEditor();
|
||||
|
||||
AppSettings settings = new AppSettings(false);
|
||||
settings.setTitle("SimArboreal Tree Editor");
|
||||
settings.setSettingsDialogImage("/com/simsilica/arboreal/images/TreeEditor-Splash.png");
|
||||
|
||||
try {
|
||||
BufferedImage[] icons = new BufferedImage[] {
|
||||
ImageIO.read( TreeEditor.class.getResource( "/com/simsilica/arboreal/images/TreeEditor-icon-128.png" ) ),
|
||||
ImageIO.read( TreeEditor.class.getResource( "/com/simsilica/arboreal/images/TreeEditor-icon-32.png" ) ),
|
||||
ImageIO.read( TreeEditor.class.getResource( "/com/simsilica/arboreal/images/TreeEditor-icon-16.png" ) )
|
||||
};
|
||||
settings.setIcons(icons);
|
||||
} catch( IOException e ) {
|
||||
log.warn( "Error loading globe icons", e );
|
||||
}
|
||||
|
||||
main.setSettings(settings);
|
||||
|
||||
main.start();
|
||||
}
|
||||
|
||||
public TreeEditor() {
|
||||
super(new StatsAppState(), new DebugKeysAppState(),
|
||||
new BuilderState(1, 1),
|
||||
new MovementState(),
|
||||
new DebugHudState(),
|
||||
new TreeOptionsState(),
|
||||
new LightingState(),
|
||||
new GroundState(),
|
||||
new SkyState(),
|
||||
new AvatarState(),
|
||||
new TreeParametersState(),
|
||||
new ForestGridState(),
|
||||
new AtlasGeneratorState(),
|
||||
new FileActionsState(),
|
||||
new PostProcessorState(),
|
||||
new ScreenshotAppState("", System.currentTimeMillis()));
|
||||
}
|
||||
|
||||
public void toggleRecordVideo() {
|
||||
VideoRecorderAppState recorder = stateManager.getState(VideoRecorderAppState.class);
|
||||
if( recorder == null ) {
|
||||
recorder = new VideoRecorderAppState(new File("trees-" + System.currentTimeMillis() + ".avi"));
|
||||
stateManager.attach(recorder);
|
||||
} else {
|
||||
stateManager.detach(recorder);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simpleInitApp() {
|
||||
|
||||
GuiGlobals.initialize(this);
|
||||
|
||||
cam.setLocation(new Vector3f(0, 1.8f, 10));
|
||||
|
||||
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
|
||||
MainFunctions.initializeDefaultMappings(inputMapper);
|
||||
inputMapper.activateGroup( MainFunctions.GROUP );
|
||||
MovementFunctions.initializeDefaultMappings(inputMapper);
|
||||
|
||||
inputMapper.addDelegate(MainFunctions.F_RECORD_VIDEO, this, "toggleRecordVideo");
|
||||
|
||||
/*
|
||||
// Now create the normal simple test scene
|
||||
Box b = new Box(1, 1, 1);
|
||||
Geometry geom = new Geometry("Box", b);
|
||||
|
||||
Material mat = new Material(assetManager, "Common/MatDefs/Light/Lighting.j3md");
|
||||
mat.setColor("Diffuse", ColorRGBA.Blue);
|
||||
mat.setColor("Ambient", ColorRGBA.Green);
|
||||
mat.setBoolean("UseMaterialColors", true);
|
||||
geom.setMaterial(mat);
|
||||
|
||||
rootNode.attachChild(geom);
|
||||
*/
|
||||
|
||||
BaseStyles.loadGlassStyle();
|
||||
|
||||
TreeOptionsState treeOptions = stateManager.getState(TreeOptionsState.class);
|
||||
treeOptions.addOptionToggle("Grass", stateManager.getState(GroundState.class), "setShowGrass");
|
||||
treeOptions.addOptionToggle("Ground Atm.", stateManager.getState(GroundState.class), "setUseScattering");
|
||||
treeOptions.addOptionToggle("Tree Atm.", stateManager.getState(ForestGridState.class), "setUseScattering");
|
||||
treeOptions.addOptionToggle("Sky", stateManager.getState(SkyState.class), "setEnabled");
|
||||
stateManager.getState(SkyState.class).setEnabled(false);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.SimpleApplication;
|
||||
import com.simsilica.lemur.Button;
|
||||
import com.simsilica.lemur.Checkbox;
|
||||
import com.simsilica.lemur.Command;
|
||||
import com.simsilica.lemur.Container;
|
||||
import com.simsilica.lemur.GuiGlobals;
|
||||
import com.simsilica.lemur.RollupPanel;
|
||||
import com.simsilica.lemur.TabbedPanel;
|
||||
import com.simsilica.lemur.component.BorderLayout;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import com.simsilica.lemur.input.InputMapper;
|
||||
import com.simsilica.lemur.style.ElementId;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class TreeOptionsState extends BaseAppState {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(TreeOptionsState.class);
|
||||
|
||||
private Container mainWindow;
|
||||
private Container mainContents;
|
||||
|
||||
private Container viewSettingsPanel;
|
||||
private Container actionsPanel;
|
||||
private Container checkboxPanel;
|
||||
private TabbedPanel tabs;
|
||||
|
||||
private List<Checkbox> optionToggles = new ArrayList<Checkbox>();
|
||||
private Map<String, Checkbox> optionToggleMap = new HashMap<String, Checkbox>();
|
||||
|
||||
private int columns = 3;
|
||||
|
||||
public TreeOptionsState() {
|
||||
}
|
||||
|
||||
public Container getContents() {
|
||||
return mainContents;
|
||||
}
|
||||
|
||||
public Container getViewSettings() {
|
||||
return viewSettingsPanel;
|
||||
}
|
||||
|
||||
public TabbedPanel getParameterTabs() {
|
||||
return tabs;
|
||||
}
|
||||
|
||||
public Checkbox addOptionToggle( String name, Object target, String method ) {
|
||||
Checkbox cb = new Checkbox(name, "glass");
|
||||
cb.addClickCommands(new ToggleHandler(target, method));
|
||||
|
||||
int column = optionToggles.size() % columns;
|
||||
if( checkboxPanel != null ) {
|
||||
if( column == 0 ) {
|
||||
checkboxPanel.addChild(cb);
|
||||
} else {
|
||||
checkboxPanel.addChild(cb, column);
|
||||
}
|
||||
}
|
||||
optionToggles.add(cb);
|
||||
optionToggleMap.put(name, cb);
|
||||
return cb;
|
||||
}
|
||||
|
||||
public void toggleHud() {
|
||||
setEnabled( !isEnabled() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
// Always register for our hot key as long as
|
||||
// we are attached.
|
||||
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
|
||||
inputMapper.addDelegate( MainFunctions.F_HUD, this, "toggleHud" );
|
||||
|
||||
mainWindow = new Container(new BorderLayout(), new ElementId("window"), "glass");
|
||||
//mainWindow.addChild(new Label("Tree Options", mainWindow.getElementId().child("title.label"), "glass"),
|
||||
// BorderLayout.Position.North);
|
||||
mainWindow.setLocalTranslation(10, app.getCamera().getHeight() - 10, 0);
|
||||
|
||||
mainContents = mainWindow.addChild(new Container(mainWindow.getElementId().child("contents.container"), "glass"),
|
||||
BorderLayout.Position.Center);
|
||||
|
||||
//mainContents.addChild(new Label("Visualization Options:", "glass"));
|
||||
actionsPanel = mainContents.addChild(new Container());
|
||||
|
||||
Container visOptions = new Container();
|
||||
RollupPanel visRollup = new RollupPanel("Visualization Options", visOptions,
|
||||
new ElementId("root.rollup"), "glass");
|
||||
mainContents.addChild(visRollup);
|
||||
viewSettingsPanel = visOptions.addChild(new Container());
|
||||
checkboxPanel = visOptions.addChild(new Container());
|
||||
|
||||
// Add any toggles that were added before init
|
||||
int i = 0;
|
||||
for( Checkbox cb : optionToggles ) {
|
||||
int column = (i++) % columns;
|
||||
if( column == 0 ) {
|
||||
checkboxPanel.addChild(cb);
|
||||
} else {
|
||||
checkboxPanel.addChild(cb, column);
|
||||
}
|
||||
}
|
||||
|
||||
//mainContents.addChild(new Label("Tree Parameters:", "glass"));
|
||||
//tabs = mainContents.addChild(new TabbedPanel("glass"));
|
||||
tabs = new TabbedPanel("glass");
|
||||
mainContents.addChild(new RollupPanel("Tree Parameters", tabs,
|
||||
new ElementId("root.rollup"), "glass"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
InputMapper inputMapper = GuiGlobals.getInstance().getInputMapper();
|
||||
inputMapper.removeDelegate( MainFunctions.F_HUD, this, "toggleHud" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
((SimpleApplication)getApplication()).getGuiNode().attachChild(mainWindow);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
mainWindow.removeFromParent();
|
||||
}
|
||||
|
||||
private class ToggleHandler implements Command<Button> {
|
||||
private Object object;
|
||||
private Method method;
|
||||
|
||||
public ToggleHandler( Object object, String methodName ) {
|
||||
this.object = object;
|
||||
try {
|
||||
this.method = object.getClass().getMethod(methodName, Boolean.TYPE);
|
||||
} catch( Exception e ) {
|
||||
throw new RuntimeException("Error retrieving method for:" + methodName, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute( Button source ) {
|
||||
try {
|
||||
method.invoke(object, ((Checkbox)source).isChecked());
|
||||
} catch( Exception e ) {
|
||||
throw new RuntimeException("Error sending state for:" + object + "->" + method, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.simsilica.arboreal.ui.CheckboxModelGroup;
|
||||
import com.simsilica.lemur.Axis;
|
||||
import com.simsilica.lemur.Checkbox;
|
||||
import com.simsilica.lemur.Container;
|
||||
import com.simsilica.lemur.FillMode;
|
||||
import com.simsilica.lemur.Label;
|
||||
import com.simsilica.lemur.RollupPanel;
|
||||
import com.simsilica.lemur.TabbedPanel;
|
||||
import com.simsilica.lemur.component.BorderLayout;
|
||||
import com.simsilica.lemur.component.SpringGridLayout;
|
||||
import com.simsilica.lemur.core.VersionedHolder;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import com.simsilica.lemur.event.BaseAppState;
|
||||
import com.simsilica.lemur.props.PropertyPanel;
|
||||
import com.simsilica.lemur.style.ElementId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Manages the main TreeParameters objects and the
|
||||
* editor panels for modifying its values.
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class TreeParametersState extends BaseAppState {
|
||||
|
||||
static Logger log = LoggerFactory.getLogger(TreeParametersState.class);
|
||||
|
||||
private TreeParameters treeParameters;
|
||||
private VersionedHolder<TreeParameters> treeParametersHolder = new VersionedHolder<TreeParameters>();
|
||||
|
||||
// Keep track of the panels so that we can easily refresh them
|
||||
// when loading new files or otherwise changing parameters
|
||||
// outside of the UI.
|
||||
private List<PropertyPanel> treePanels;
|
||||
|
||||
// Keep the array of version refs for easy tracking
|
||||
// of changes across all parameters.
|
||||
private VersionedReference[] versions;
|
||||
|
||||
private VersionedReference<Boolean> building;
|
||||
private LevelStats[] levelStats;
|
||||
|
||||
public TreeParametersState() {
|
||||
this.treeParameters = new TreeParameters(5);
|
||||
this.treeParametersHolder.setObject(treeParameters);
|
||||
}
|
||||
|
||||
public TreeParameters getTreeParameters() {
|
||||
return treeParameters;
|
||||
}
|
||||
|
||||
public VersionedReference<TreeParameters> getTreeParametersRef() {
|
||||
return treeParametersHolder.createReference();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize( Application app ) {
|
||||
|
||||
List<VersionedReference> versionsList = new ArrayList<VersionedReference>();
|
||||
|
||||
TabbedPanel tabs = getState(TreeOptionsState.class).getParameterTabs();
|
||||
|
||||
treePanels = new ArrayList<PropertyPanel>();
|
||||
PropertyPanel properties;
|
||||
|
||||
// Biggest real life tree is 25 feet in diameter... or 7.62 meters. We'll
|
||||
// let things get more ridiculous
|
||||
properties = new PropertyPanel("glass");
|
||||
treePanels.add(properties);
|
||||
versionsList.add(properties.createReference());
|
||||
tabs.addTab("Tree", properties);
|
||||
|
||||
properties.addIntProperty("Seed", treeParameters, "seed", 0, 100, 1);
|
||||
properties.addFloatProperty("Radius (m)", treeParameters, "trunkRadius", 0.05f, 10f, 0.05f);
|
||||
properties.addFloatProperty("Height (m)", treeParameters, "trunkHeight", 0.1f, 50f, 0.1f);
|
||||
properties.addFloatProperty("Root Height (m)", treeParameters, "rootHeight", 0f, 10f, 0.1f);
|
||||
properties.addFloatProperty("Y Offset (m)", treeParameters, "YOffset", 0f, 10f, 0.01f);
|
||||
properties.addIntProperty("Texture U Repeat", treeParameters, "textureURepeat", 1, 12, 1);
|
||||
properties.addFloatProperty("Texture V Scale", treeParameters, "textureVScale", 0.01f, 10f, 0.1f);
|
||||
properties.addFloatProperty("Wind Flex Height (m)", treeParameters, "flexHeight", 0.0f, 50, 0.01f);
|
||||
properties.addFloatProperty("Trunk Flex.", treeParameters, "trunkFlexibility", 0.0f, 5.0f, 0.001f);
|
||||
properties.addFloatProperty("Branch Flex.", treeParameters, "branchFlexibility", 0.0f, 5.0f, 0.001f);
|
||||
|
||||
Container branchPanels = new Container("glass");
|
||||
tabs.addTab("Branches", branchPanels);
|
||||
|
||||
// Now the sub-panels for each branch
|
||||
RollupPanel first = null;
|
||||
CheckboxModelGroup rollupGroup = new CheckboxModelGroup();
|
||||
for( int i = 0; i < treeParameters.getDepth(); i++ ) {
|
||||
BranchParameters branch = treeParameters.getBranch(i);
|
||||
|
||||
Container nested = new Container("glass");
|
||||
properties = new PropertyPanel(new ElementId("nestedProperties"), "glass");
|
||||
nested.addChild(properties);
|
||||
|
||||
treePanels.add(properties);
|
||||
versionsList.add(properties.createReference());
|
||||
RollupPanel rollup = branchPanels.addChild(new RollupPanel("Level " + i, nested, "glass"));
|
||||
rollup.setOpenModel(rollupGroup.addChild(rollup.getOpenModel()));
|
||||
if( i > 0 ) {
|
||||
// The first panel cannot be disabled so we only
|
||||
// apply the enabled/disabled checkbox to the others
|
||||
properties.setEnabledProperty(branch, "enabled");
|
||||
rollup.getTitleContainer().addChild(new Checkbox("", properties.getEnabledModel(), "glass"));
|
||||
properties.addBooleanField("Inherit", branch, "inherit");
|
||||
rollup.setOpen(false);
|
||||
} else {
|
||||
first = rollup;
|
||||
}
|
||||
|
||||
properties.addIntField("Radial Segments", branch, "radialSegments", 3, 24, 1);
|
||||
properties.addIntField("Length Segments", branch, "lengthSegments", 1, 10, 1);
|
||||
properties.addFloatField("Segment Variation (*)", branch, "segmentVariation", 0, 1, 0.01f);
|
||||
properties.addFloatField("Taper (*)", branch, "taper", 0.1f, 1f, 0.01f);
|
||||
properties.addFloatField("Twist (rads)", branch, "twist", 0f, FastMath.PI, 0.01f);
|
||||
properties.addFloatField("Gravity (*)", branch, "gravity", -1f, 1f, 0.1f);
|
||||
|
||||
|
||||
properties.addIntField("Side Joints", branch, "sideJointCount", 0, 8, 1);
|
||||
|
||||
properties = new PropertyPanel(new ElementId("nestedProperties"), "glass");
|
||||
nested.addChild(properties);
|
||||
treePanels.add(properties);
|
||||
versionsList.add(properties.createReference());
|
||||
|
||||
properties.addFloatField("Angle (rads)", branch, "sideJointStartAngle", 0, FastMath.PI, 0.01f);
|
||||
properties.addFloatField("Inclination (rads)", branch, "inclination", 0f, FastMath.HALF_PI, 0.01f);
|
||||
properties.addFloatField("Radius Scale (*)", branch, "radiusScale", 0.01f, 2f, 0.01f);
|
||||
properties.addFloatField("Length Scale (*)", branch, "lengthScale", 0.01f, 5f, 0.01f);
|
||||
|
||||
properties.addBooleanField("Tip Joint", branch, "hasEndJoint");
|
||||
properties.addFloatField("Tip Rotation (rads)", branch, "tipRotation", 0f, FastMath.PI, 0.01f);
|
||||
}
|
||||
first.setOpen(true);
|
||||
|
||||
|
||||
Container rootPanels = new Container("glass");
|
||||
tabs.addTab("Roots", rootPanels);
|
||||
|
||||
// Now the sub-panels for each root
|
||||
first = null;
|
||||
rollupGroup = new CheckboxModelGroup();
|
||||
for( int i = 0; i < treeParameters.getDepth(); i++ ) {
|
||||
BranchParameters branch = treeParameters.getRoot(i);
|
||||
|
||||
Container nested = new Container("glass");
|
||||
properties = new PropertyPanel(new ElementId("nestedProperties"), "glass");
|
||||
nested.addChild(properties);
|
||||
|
||||
treePanels.add(properties);
|
||||
versionsList.add(properties.createReference());
|
||||
RollupPanel rollup = rootPanels.addChild(new RollupPanel("Level " + i, nested, "glass"));
|
||||
rollup.setOpenModel(rollupGroup.addChild(rollup.getOpenModel()));
|
||||
if( i > 0 ) {
|
||||
// The first panel cannot be disabled so we only
|
||||
// apply the enabled/disabled checkbox to the others
|
||||
properties.setEnabledProperty(branch, "enabled");
|
||||
rollup.getTitleContainer().addChild(new Checkbox("", properties.getEnabledModel(), "glass"));
|
||||
properties.addBooleanField("Inherit", branch, "inherit");
|
||||
rollup.setOpen(false);
|
||||
} else {
|
||||
first = rollup;
|
||||
}
|
||||
|
||||
properties.addIntField("Radial Segments", branch, "radialSegments", 3, 24, 1);
|
||||
properties.addIntField("Length Segments", branch, "lengthSegments", 1, 10, 1);
|
||||
properties.addFloatField("Segment Variation (*)", branch, "segmentVariation", 0, 1, 0.01f);
|
||||
properties.addFloatField("Taper (*)", branch, "taper", 0.1f, 1f, 0.01f);
|
||||
properties.addFloatField("Twist (rads)", branch, "twist", 0f, FastMath.PI, 0.01f);
|
||||
properties.addFloatField("Gravity (*)", branch, "gravity", -1f, 1f, 0.1f);
|
||||
|
||||
properties.addIntField("Side Joints", branch, "sideJointCount", 0, 8, 1);
|
||||
|
||||
properties = new PropertyPanel(new ElementId("nestedProperties"), "glass");
|
||||
nested.addChild(properties);
|
||||
treePanels.add(properties);
|
||||
versionsList.add(properties.createReference());
|
||||
|
||||
properties.addFloatField("Angle (rads)", branch, "sideJointStartAngle", 0, FastMath.PI, 0.01f);
|
||||
properties.addFloatField("Inclination (rads)", branch, "inclination", 0f, FastMath.HALF_PI, 0.01f);
|
||||
properties.addFloatField("Radius Scale (*)", branch, "radiusScale", 0.01f, 2f, 0.01f);
|
||||
properties.addFloatField("Length Scale (*)", branch, "lengthScale", 0.01f, 4f, 0.01f);
|
||||
|
||||
properties.addBooleanField("Tip Joint", branch, "hasEndJoint");
|
||||
properties.addFloatField("Tip Rotation (rads)", branch, "tipRotation", 0f, FastMath.PI, 0.01f);
|
||||
}
|
||||
first.setOpen(true);
|
||||
|
||||
// And now the leaves panel
|
||||
properties = new PropertyPanel("glass");
|
||||
treePanels.add(properties);
|
||||
versionsList.add(properties.createReference());
|
||||
tabs.addTab("Leaves", properties);
|
||||
properties.addBooleanProperty("Enabled", treeParameters, "generateLeaves");
|
||||
properties.addFloatProperty("Size (m)", treeParameters, "leafScale", 0.1f, 10f, 0.1f);
|
||||
|
||||
|
||||
|
||||
// LOD tab
|
||||
Container lodPanels = new Container("glass");
|
||||
tabs.addTab("LOD", lodPanels);
|
||||
|
||||
levelStats = new LevelStats[treeParameters.getLodCount()];
|
||||
|
||||
first = null;
|
||||
rollupGroup = new CheckboxModelGroup();
|
||||
for( int i = 0; i < treeParameters.getLodCount(); i++ ) {
|
||||
LevelOfDetailParameters lod = treeParameters.getLod(i);
|
||||
|
||||
String name = (i == 0) ? "Highest" : ("Level " + i);
|
||||
|
||||
Container nested = new Container(new BorderLayout());
|
||||
levelStats[i] = nested.addChild(new LevelStats(i), BorderLayout.Position.South);
|
||||
properties = new PropertyPanel("glass");
|
||||
nested.addChild(properties, BorderLayout.Position.Center);
|
||||
treePanels.add(properties);
|
||||
versionsList.add(properties.createReference());
|
||||
RollupPanel rollup = lodPanels.addChild(new RollupPanel(name, nested, "glass"));
|
||||
rollup.setOpenModel(rollupGroup.addChild(rollup.getOpenModel()));
|
||||
if( i == 0 ) {
|
||||
first = rollup;
|
||||
} else {
|
||||
rollup.setOpen(false);
|
||||
}
|
||||
|
||||
properties.addFloatField("Distance (m)", lod, "distance", 0, 1000, 1);
|
||||
properties.addIntField("Branch Depth", lod, "branchDepth", 1, treeParameters.getDepth(), 1);
|
||||
properties.addIntField("Root Depth", lod, "rootDepth", 1, treeParameters.getDepth(), 1);
|
||||
properties.addIntField("Max Radial Segments", lod, "maxRadialSegments", 3, 24, 1);
|
||||
properties.addEnumField("Mesh Type", lod, "reduction");
|
||||
}
|
||||
first.setOpen(true);
|
||||
|
||||
|
||||
versions = new VersionedReference[versionsList.size()];
|
||||
versions = versionsList.toArray(versions);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup( Application app ) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void enable() {
|
||||
}
|
||||
|
||||
float nextUpdateCheck = 0.1f;
|
||||
@Override
|
||||
public void update( float tpf ) {
|
||||
|
||||
nextUpdateCheck += tpf;
|
||||
if( nextUpdateCheck > 0.1f ) {
|
||||
boolean changed = false;
|
||||
for( VersionedReference ref : versions ) {
|
||||
if( ref.update() ) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if( changed ) {
|
||||
treeParametersHolder.incrementVersion();
|
||||
}
|
||||
}
|
||||
|
||||
// See if we need to update the LOD stats
|
||||
if( building == null ) {
|
||||
// We haven't retrieved it yet. We get attached before
|
||||
// ForedGridState so we have to grab this lazily.
|
||||
building = getState(ForestGridState.class).getBuildingRef();
|
||||
refreshStats();
|
||||
} else if( building.update() ) {
|
||||
refreshStats();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void disable() {
|
||||
}
|
||||
|
||||
public void refreshTreePanels() {
|
||||
for( PropertyPanel p : treePanels ) {
|
||||
p.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
protected void refreshStats() {
|
||||
boolean ready = !building.get();
|
||||
for( LevelStats stats : levelStats ) {
|
||||
stats.update(ready);
|
||||
}
|
||||
}
|
||||
|
||||
private class LevelStats extends Container {
|
||||
|
||||
int level;
|
||||
Label verts;
|
||||
Label tris;
|
||||
|
||||
public LevelStats( int level ) {
|
||||
super(new SpringGridLayout(Axis.X, Axis.Y, FillMode.Even, FillMode.Even));
|
||||
this.level = level;
|
||||
this.verts = addChild(new Label("LOD verts: ???", "glass"));
|
||||
this.tris = addChild(new Label("tris: ???", "glass"));
|
||||
}
|
||||
|
||||
protected void update( boolean ready ) {
|
||||
if( ready ) {
|
||||
TreeBuilderReference tree = getState(ForestGridState.class).getMainTree();
|
||||
verts.setText("LOD verts: " + tree.getVertexCount(level));
|
||||
tris.setText("tris: " + tree.getTriangleCount(level));
|
||||
} else {
|
||||
verts.setText("LOD verts: ???");
|
||||
tris.setText("tris: ???");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 238 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 750 B |
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright (c) 2014, Simsilica, LLC
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* 3. Neither the name of the copyright holder nor the names of its
|
||||
* contributors may be used to endorse or promote products derived
|
||||
* from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package com.simsilica.arboreal.ui;
|
||||
|
||||
import com.jme3.util.SafeArrayList;
|
||||
import com.simsilica.lemur.CheckboxModel;
|
||||
import com.simsilica.lemur.DefaultCheckboxModel;
|
||||
import com.simsilica.lemur.core.VersionedReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* Provides checkbox models that are linked so that
|
||||
* only one is checked at a time.
|
||||
*
|
||||
* @author Paul Speed
|
||||
*/
|
||||
public class CheckboxModelGroup {
|
||||
|
||||
private List<CheckboxModel> models = new SafeArrayList<CheckboxModel>(CheckboxModel.class);
|
||||
private List<VersionedReference<Boolean>> refs = new ArrayList<VersionedReference<Boolean>>();
|
||||
|
||||
public CheckboxModelGroup() {
|
||||
}
|
||||
|
||||
public CheckboxModel createChild( boolean checked ) {
|
||||
return addChild(new DefaultCheckboxModel(checked));
|
||||
}
|
||||
|
||||
public CheckboxModel addChild( CheckboxModel model ) {
|
||||
int index = models.size();
|
||||
models.add(model);
|
||||
refs.add(model.createReference());
|
||||
if( model.isChecked() ) {
|
||||
updateChecked(index);
|
||||
}
|
||||
return new CheckboxModelWrapper(model);
|
||||
}
|
||||
|
||||
public void removeChild( CheckboxModel model ) {
|
||||
int index = models.indexOf(model);
|
||||
if( index < 0 ) {
|
||||
return;
|
||||
}
|
||||
models.remove(index);
|
||||
refs.remove(index);
|
||||
}
|
||||
|
||||
public void update() {
|
||||
for( int i = 0; i < refs.size(); i++ ) {
|
||||
VersionedReference<Boolean> ref = refs.get(i);
|
||||
if( ref.update() && ref.get() ) {
|
||||
updateChecked(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateChecked( int index ) {
|
||||
|
||||
for( int i = 0; i < models.size(); i++ ) {
|
||||
if( i == index ) {
|
||||
continue;
|
||||
}
|
||||
models.get(i).setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected class CheckboxModelWrapper implements CheckboxModel {
|
||||
private CheckboxModel delegate;
|
||||
|
||||
public CheckboxModelWrapper( CheckboxModel delegate ) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChecked( boolean b ) {
|
||||
delegate.setChecked(b);
|
||||
if( b ) {
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChecked() {
|
||||
return delegate.isChecked();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVersion() {
|
||||
return delegate.getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean getObject() {
|
||||
return delegate.getObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public VersionedReference<Boolean> createReference() {
|
||||
return new VersionedReference<Boolean>(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CheckboxModelWrapper[" + delegate + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
|
||||
import com.simsilica.lemur.*;
|
||||
import com.simsilica.lemur.Button.ButtonAction;
|
||||
import com.simsilica.lemur.component.*;
|
||||
|
||||
def gradient = TbtQuadBackgroundComponent.create(
|
||||
texture( name:"/com/simsilica/lemur/icons/bordered-gradient.png",
|
||||
generateMips:false ),
|
||||
1, 1, 1, 126, 126,
|
||||
1f, false );
|
||||
|
||||
def bevel = TbtQuadBackgroundComponent.create(
|
||||
texture( name:"/com/simsilica/lemur/icons/bevel-quad.png",
|
||||
generateMips:false ),
|
||||
0.125f, 8, 8, 119, 119,
|
||||
1f, false );
|
||||
|
||||
def border = TbtQuadBackgroundComponent.create(
|
||||
texture( name:"/com/simsilica/lemur/icons/border.png",
|
||||
generateMips:false ),
|
||||
1, 1, 1, 6, 6,
|
||||
1f, false );
|
||||
def border2 = TbtQuadBackgroundComponent.create(
|
||||
texture( name:"/com/simsilica/lemur/icons/border.png",
|
||||
generateMips:false ),
|
||||
1, 2, 2, 6, 6,
|
||||
1f, false );
|
||||
|
||||
def doubleGradient = new QuadBackgroundComponent( color(0.5, 0.75, 0.85, 0.5) );
|
||||
doubleGradient.texture = texture( name:"/com/simsilica/lemur/icons/double-gradient-128.png",
|
||||
generateMips:false )
|
||||
|
||||
selector( "glass" ) {
|
||||
fontSize = 14
|
||||
}
|
||||
|
||||
selector( "label", "glass" ) {
|
||||
//textVAlignment = VAlignment.Center
|
||||
//intsets = new Insets3f(2, 0, 0, 0);
|
||||
//background = bevel;
|
||||
insets = new Insets3f( 2, 2, 0, 2 );
|
||||
color = color(0.5, 0.75, 0.75, 0.85)
|
||||
//background = border;
|
||||
}
|
||||
|
||||
selector( "container", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}
|
||||
|
||||
selector( "nestedProperties.container", "glass" ) {
|
||||
background = border2.clone();
|
||||
background.setColor(color(0.0, 0.0, 0.0, 0.5))
|
||||
}
|
||||
|
||||
selector( "stats", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}
|
||||
|
||||
selector( "slider", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}
|
||||
|
||||
def pressedCommand = new Command<Button>() {
|
||||
public void execute( Button source ) {
|
||||
if( source.isPressed() ) {
|
||||
source.move(1, -1, 0);
|
||||
} else {
|
||||
source.move(-1, 1, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
def stdButtonCommands = [
|
||||
(ButtonAction.Down):[pressedCommand],
|
||||
(ButtonAction.Up):[pressedCommand]
|
||||
];
|
||||
|
||||
selector( "title", "glass" ) {
|
||||
color = color(0.8, 0.9, 1, 0.85f)
|
||||
highlightColor = color(1, 0.8, 1, 0.85f)
|
||||
shadowColor = color(0, 0, 0, 0.75f)
|
||||
shadowOffset = new com.jme3.math.Vector3f(2, -2, 1);
|
||||
background = new QuadBackgroundComponent( color(0.5, 0.75, 0.85, 0.5) );
|
||||
background.texture = texture( name:"/com/simsilica/lemur/icons/double-gradient-128.png",
|
||||
generateMips:false )
|
||||
//background.setMargin(2, 2);
|
||||
insets = new Insets3f( 2, 2, 2, 2 );
|
||||
|
||||
buttonCommands = stdButtonCommands;
|
||||
}
|
||||
|
||||
|
||||
selector( "button", "glass" ) {
|
||||
background = gradient.clone()
|
||||
color = color(0.8, 0.9, 1, 0.85f)
|
||||
//color = color(0, 196f/255, 196f/255, 0.75f)
|
||||
background.setColor(color(0, 0.75, 0.75, 0.5))
|
||||
insets = new Insets3f( 2, 2, 2, 2 );
|
||||
|
||||
buttonCommands = stdButtonCommands;
|
||||
}
|
||||
|
||||
selector( "slider", "glass" ) {
|
||||
insets = new Insets3f( 1, 3, 1, 2 );
|
||||
}
|
||||
|
||||
selector( "slider", "button", "glass" ) {
|
||||
background = doubleGradient.clone()
|
||||
background.setColor(color(0.5, 0.75, 0.75, 0.5))
|
||||
insets = new Insets3f( 0, 0, 0, 0 );
|
||||
}
|
||||
|
||||
selector( "slider.thumb.button", "glass" ) {
|
||||
text = "[]"
|
||||
color = color(0.6, 0.8, 0.8, 0.85)
|
||||
}
|
||||
|
||||
selector( "slider.left.button", "glass" ) {
|
||||
text = "-"
|
||||
background = doubleGradient.clone()
|
||||
background.setColor(color(0.5, 0.75, 0.75, 0.5))
|
||||
background.setMargin(5, 0);
|
||||
color = color(0.6, 0.8, 0.8, 0.85)
|
||||
}
|
||||
|
||||
selector( "slider.right.button", "glass" ) {
|
||||
text = "+"
|
||||
background = doubleGradient.clone()
|
||||
background.setColor(color(0.5, 0.75, 0.75, 0.5))
|
||||
background.setMargin(4, 0);
|
||||
color = color(0.6, 0.8, 0.8, 0.85)
|
||||
}
|
||||
|
||||
selector( "checkbox", "glass" ) {
|
||||
def on = new IconComponent( "/com/simsilica/lemur/icons/Glass-check-on.png", 1f,
|
||||
0, 0, 1f, false );
|
||||
on.setColor(color(0.5, 0.9, 0.9, 0.9))
|
||||
on.setMargin(5, 0);
|
||||
def off = new IconComponent( "/com/simsilica/lemur/icons/Glass-check-off.png", 1f,
|
||||
0, 0, 1f, false );
|
||||
off.setColor(color(0.6, 0.8, 0.8, 0.8))
|
||||
off.setMargin(5, 0);
|
||||
|
||||
onView = on;
|
||||
offView = off;
|
||||
|
||||
//background = border.clone();
|
||||
//background.color = color(0, 0, 0, 1.0);
|
||||
|
||||
color = color(0.8, 0.9, 1, 0.85f)
|
||||
//insetsComponent = new DynamicInsetsComponent(1, 1, 1, 1);
|
||||
//insets = new Insets3f(0, 5, 0, 5);
|
||||
}
|
||||
|
||||
selector( "value", "label", "glass" ) {
|
||||
insets = new Insets3f( 1, 2, 0, 2 );
|
||||
textHAlignment = HAlignment.Right;
|
||||
background = border.clone();
|
||||
background.color = color(0.5, 0.75, 0.75, 0.25)
|
||||
color = color(0.6, 0.8, 0.8, 0.85)
|
||||
}
|
||||
|
||||
/*selector( "properties", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}*/
|
||||
|
||||
selector( "rollup", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}
|
||||
|
||||
selector( "window", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}
|
||||
|
||||
selector( "tabbedPanel", "glass" ) {
|
||||
activationColor = color(0.8, 0.9, 1, 0.85f)
|
||||
}
|
||||
|
||||
selector( "tabbedPanel.container", "glass" ) {
|
||||
background = null
|
||||
}
|
||||
|
||||
selector( "tab.button", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
//color = color(0.8, 0.9, 1, 0.85f)
|
||||
color = color(0.4, 0.45, 0.5, 0.85f)
|
||||
insets = new Insets3f( 4, 2, 0, 2 );
|
||||
|
||||
buttonCommands = stdButtonCommands;
|
||||
}
|
||||
|
||||
selector( "root.rollup.title", "glass" ) {
|
||||
background = new QuadBackgroundComponent( color(0.25, 0.5, 0.85, 0.5) );
|
||||
background.texture = texture( name:"/com/simsilica/lemur/icons/double-gradient-128.png",
|
||||
generateMips:false )
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.simsilica.fx;
|
||||
|
||||
import com.simsilica.arboreal.LegacyLightingState;
|
||||
|
||||
public class LightingState extends LegacyLightingState {
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.simsilica.fx.sky;
|
||||
|
||||
import com.jme3.material.Material;
|
||||
|
||||
public class AtmosphericParameters {
|
||||
public void applyGroundParameters(Material mat, boolean b) {
|
||||
// stub — sim-fx atmospheric effects not available
|
||||
}
|
||||
}
|
||||
12
simarboreal/src/main/java/com/simsilica/fx/sky/SkyState.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package com.simsilica.fx.sky;
|
||||
|
||||
import com.simsilica.arboreal.LegacySkyState;
|
||||
|
||||
public class SkyState extends LegacySkyState {
|
||||
|
||||
private final AtmosphericParameters atmosphere = new AtmosphericParameters();
|
||||
|
||||
public AtmosphericParameters getAtmosphericParameters() {
|
||||
return atmosphere;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
import com.simsilica.lemur.*;
|
||||
import com.simsilica.lemur.Button.ButtonAction;
|
||||
import com.simsilica.lemur.component.*;
|
||||
|
||||
def gradient = TbtQuadBackgroundComponent.create(
|
||||
texture( name:"/com/simsilica/lemur/icons/bordered-gradient.png",
|
||||
generateMips:false ),
|
||||
1, 1, 1, 126, 126,
|
||||
1f, false );
|
||||
|
||||
def border = TbtQuadBackgroundComponent.create(
|
||||
texture( name:"/com/simsilica/lemur/icons/border.png",
|
||||
generateMips:false ),
|
||||
1, 2, 2, 6, 6,
|
||||
1f, false );
|
||||
|
||||
|
||||
selector( "nestedProperties.container", "glass" ) {
|
||||
background = border.clone();
|
||||
background.setColor(color(0.0, 0.0, 0.0, 0.5))
|
||||
}
|
||||
|
||||
selector( "stats", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}
|
||||
|
||||
selector( "window", "glass" ) {
|
||||
background = gradient.clone()
|
||||
background.setColor(color(0.25, 0.5, 0.5, 0.5))
|
||||
}
|
||||
|
||||
selector( "root.rollup.title", "glass" ) {
|
||||
background = new QuadBackgroundComponent( color(0.25, 0.5, 0.85, 0.5) );
|
||||
background.texture = texture( name:"/com/simsilica/lemur/icons/double-gradient-128.png",
|
||||
generateMips:false )
|
||||
}
|
||||