├── assets └── MatDefs │ ├── Grass.frag │ ├── Grass.j3md │ ├── Grass.vert │ ├── TrilinearLighting.frag │ ├── TrilinearLighting.j3md │ └── TrilinearLighting.vert ├── build.xml ├── nbproject ├── assets-impl.xml ├── build-impl.xml ├── genfiles.properties ├── project.properties └── project.xml └── src └── main └── java ├── com └── simsilica │ └── iso │ ├── DensityVolume.java │ ├── IsoTerrainZone.java │ ├── IsoTerrainZoneFactory.java │ ├── MeshGenerator.java │ ├── collision │ ├── Collider.java │ ├── Contact.java │ └── SimpleVolumeCollider.java │ ├── fractal │ ├── GemsFractalDensityVolume.java │ └── PerlinNoise.java │ ├── mc │ ├── MarchingCubesConstants.java │ └── MarchingCubesMeshGenerator.java │ ├── plot │ ├── BatchInstance.java │ ├── BatchTemplate.java │ ├── GrassZone.java │ ├── InstanceTemplate.java │ ├── InstancedTreeZone.java │ ├── PlotFrequencyZone.java │ └── TreeZone.java │ ├── tri │ ├── Triangle.java │ ├── TriangleProcessor.java │ └── TriangleUtils.java │ ├── util │ ├── BilinearArray.java │ ├── MatrixUtils.java │ └── MeshCompareUtil.java │ └── volume │ ├── ArrayDensityVolume.java │ ├── CachingDensityVolume.java │ └── ResamplingVolume.java └── license.txt /assets/MatDefs/Grass.frag: -------------------------------------------------------------------------------- 1 | #import "Common/ShaderLib/Parallax.glsllib" 2 | #import "Common/ShaderLib/Optics.glsllib" 3 | #define ATTENUATION 4 | //#define HQ_ATTENUATION 5 | 6 | varying vec2 texCoord; 7 | #ifdef SEPARATE_TEXCOORD 8 | varying vec2 texCoord2; 9 | #endif 10 | 11 | varying vec3 AmbientSum; 12 | varying vec4 DiffuseSum; 13 | varying vec3 SpecularSum; 14 | 15 | varying float vDistance; 16 | 17 | 18 | #ifndef VERTEX_LIGHTING 19 | uniform vec4 g_LightDirection; 20 | //varying vec3 vPosition; 21 | varying vec3 vViewDir; 22 | varying vec4 vLightDir; 23 | varying vec3 lightVec; 24 | #else 25 | varying vec2 vertexLightValues; 26 | #endif 27 | 28 | #ifdef DIFFUSEMAP 29 | uniform sampler2D m_DiffuseMap; 30 | #endif 31 | 32 | #ifdef SPECULARMAP 33 | uniform sampler2D m_SpecularMap; 34 | #endif 35 | 36 | #ifdef PARALLAXMAP 37 | uniform sampler2D m_ParallaxMap; 38 | #endif 39 | #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 40 | uniform float m_ParallaxHeight; 41 | #endif 42 | 43 | #ifdef LIGHTMAP 44 | uniform sampler2D m_LightMap; 45 | #endif 46 | 47 | #ifdef NORMALMAP 48 | uniform sampler2D m_NormalMap; 49 | #else 50 | varying vec3 vNormal; 51 | #endif 52 | 53 | #ifdef ALPHAMAP 54 | uniform sampler2D m_AlphaMap; 55 | #endif 56 | 57 | #ifdef COLORRAMP 58 | uniform sampler2D m_ColorRamp; 59 | #endif 60 | 61 | uniform float m_AlphaDiscardThreshold; 62 | 63 | uniform float m_DistanceFalloff; 64 | 65 | #ifndef VERTEX_LIGHTING 66 | uniform float m_Shininess; 67 | 68 | #ifdef HQ_ATTENUATION 69 | uniform vec4 g_LightPosition; 70 | #endif 71 | 72 | #ifdef USE_REFLECTION 73 | uniform float m_ReflectionPower; 74 | uniform float m_ReflectionIntensity; 75 | varying vec4 refVec; 76 | 77 | uniform ENVMAP m_EnvMap; 78 | #endif 79 | 80 | float tangDot(in vec3 v1, in vec3 v2){ 81 | float d = dot(v1,v2); 82 | #ifdef V_TANGENT 83 | d = 1.0 - d*d; 84 | return step(0.0, d) * sqrt(d); 85 | #else 86 | return d; 87 | #endif 88 | } 89 | 90 | float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){ 91 | #ifdef MINNAERT 92 | float NdotL = max(0.0, dot(norm, lightdir)); 93 | float NdotV = max(0.0, dot(norm, viewdir)); 94 | return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5; 95 | #else 96 | return max(0.0, dot(norm, lightdir)); 97 | #endif 98 | } 99 | 100 | float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){ 101 | // NOTE: check for shiny <= 1 removed since shininess is now 102 | // 1.0 by default (uses matdefs default vals) 103 | #ifdef LOW_QUALITY 104 | // Blinn-Phong 105 | // Note: preferably, H should be computed in the vertex shader 106 | vec3 H = (viewdir + lightdir) * vec3(0.5); 107 | return pow(max(tangDot(H, norm), 0.0), shiny); 108 | #elif defined(WARDISO) 109 | // Isotropic Ward 110 | vec3 halfVec = normalize(viewdir + lightdir); 111 | float NdotH = max(0.001, tangDot(norm, halfVec)); 112 | float NdotV = max(0.001, tangDot(norm, viewdir)); 113 | float NdotL = max(0.001, tangDot(norm, lightdir)); 114 | float a = tan(acos(NdotH)); 115 | float p = max(shiny/128.0, 0.001); 116 | return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL))); 117 | #else 118 | // Standard Phong 119 | vec3 R = reflect(-lightdir, norm); 120 | return pow(max(tangDot(R, viewdir), 0.0), shiny); 121 | #endif 122 | } 123 | 124 | vec2 computeLighting(in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){ 125 | float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir); 126 | float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess); 127 | 128 | #ifdef HQ_ATTENUATION 129 | float att = clamp(1.0 - g_LightPosition.w * length(lightVec), 0.0, 1.0); 130 | #else 131 | float att = vLightDir.w; 132 | #endif 133 | 134 | if (m_Shininess <= 1.0) { 135 | specularFactor = 0.0; // should be one instruction on most cards .. 136 | } 137 | 138 | specularFactor *= diffuseFactor; 139 | 140 | return vec2(diffuseFactor, specularFactor) * vec2(att); 141 | } 142 | #endif 143 | 144 | void main(){ 145 | vec2 newTexCoord; 146 | 147 | #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 148 | 149 | #ifdef STEEP_PARALLAX 150 | #ifdef NORMALMAP_PARALLAX 151 | //parallax map is stored in the alpha channel of the normal map 152 | newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); 153 | #else 154 | //parallax map is a texture 155 | newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); 156 | #endif 157 | #else 158 | #ifdef NORMALMAP_PARALLAX 159 | //parallax map is stored in the alpha channel of the normal map 160 | newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord, m_ParallaxHeight); 161 | #else 162 | //parallax map is a texture 163 | newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord, m_ParallaxHeight); 164 | #endif 165 | #endif 166 | #else 167 | newTexCoord = texCoord; 168 | #endif 169 | 170 | #ifdef DIFFUSEMAP 171 | vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord); 172 | #else 173 | vec4 diffuseColor = vec4(1.0); 174 | #endif 175 | 176 | float alpha = DiffuseSum.a * diffuseColor.a; 177 | #ifdef ALPHAMAP 178 | alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r; 179 | #endif 180 | 181 | // Adjust alpha for distance 182 | // We want full alpha from 0 to mid and fading alpha from 183 | // mid falloff 184 | float mid = m_DistanceFalloff * 0.75; 185 | float fade = clamp((m_DistanceFalloff - vDistance) / mid, 0.0, 1.0); 186 | //float fade = min(1.0, (m_DistanceFalloff - vDistance) / mid); 187 | fade = fade * fade; // more gradual fading 188 | 189 | #ifdef USE_DISCARD 190 | if( (alpha * fade) < m_AlphaDiscardThreshold ) { 191 | discard; 192 | } 193 | #endif 194 | 195 | #ifdef USE_DARKENING 196 | float darken = min(1.0, 2.0 * alpha); 197 | 198 | alpha *= fade; 199 | 200 | #ifdef USE_TAPER 201 | // Far away... we want the 202 | // tapering to be less distinct 203 | //float mid2 = m_DistanceFalloff * 0.5; 204 | //float distFactor = min(1.0, ((m_DistanceFalloff - vDistance) / mid2); 205 | float distFactor = vDistance / (m_DistanceFalloff * 0.25); 206 | darken *= min(1.0, texCoord.y * distFactor); 207 | #endif 208 | 209 | float colorMix = min(0.7, alpha); 210 | 211 | // Mix some darker color in as the fade kicks in to avoid the gray borders 212 | //diffuseColor = mix(vec4(0.2, 0.25, 0.05, fade), diffuseColor, alpha); 213 | diffuseColor = mix(vec4(0.2 * darken, 0.25 * darken, 0.05 * darken, fade), diffuseColor, colorMix); 214 | #endif 215 | 216 | #ifndef VERTEX_LIGHTING 217 | float spotFallOff = 1.0; 218 | 219 | #if __VERSION__ >= 110 220 | // allow use of control flow 221 | if(g_LightDirection.w != 0.0){ 222 | #endif 223 | 224 | vec3 L = normalize(lightVec.xyz); 225 | vec3 spotdir = normalize(g_LightDirection.xyz); 226 | float curAngleCos = dot(-L, spotdir); 227 | float innerAngleCos = floor(g_LightDirection.w) * 0.001; 228 | float outerAngleCos = fract(g_LightDirection.w); 229 | float innerMinusOuter = innerAngleCos - outerAngleCos; 230 | spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter; 231 | 232 | #if __VERSION__ >= 110 233 | if(spotFallOff <= 0.0){ 234 | gl_FragColor.rgb = AmbientSum * diffuseColor.rgb; 235 | gl_FragColor.a = alpha; 236 | return; 237 | }else{ 238 | spotFallOff = clamp(spotFallOff, 0.0, 1.0); 239 | } 240 | } 241 | #else 242 | spotFallOff = clamp(spotFallOff, step(g_LightDirection.w, 0.001), 1.0); 243 | #endif 244 | #endif 245 | 246 | // *********************** 247 | // Read from textures 248 | // *********************** 249 | #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) 250 | vec4 normalHeight = texture2D(m_NormalMap, newTexCoord); 251 | vec3 normal = normalize((normalHeight.xyz * vec3(2.0) - vec3(1.0))); 252 | #ifdef LATC 253 | normal.z = sqrt(1.0 - (normal.x * normal.x) - (normal.y * normal.y)); 254 | #endif 255 | //normal.y = -normal.y; 256 | #elif !defined(VERTEX_LIGHTING) 257 | vec3 normal = vNormal; 258 | #if !defined(LOW_QUALITY) && !defined(V_TANGENT) 259 | normal = normalize(normal); 260 | #endif 261 | #endif 262 | 263 | #ifdef SPECULARMAP 264 | vec4 specularColor = texture2D(m_SpecularMap, newTexCoord); 265 | #else 266 | vec4 specularColor = vec4(1.0); 267 | #endif 268 | 269 | #ifdef LIGHTMAP 270 | vec3 lightMapColor; 271 | #ifdef SEPARATE_TEXCOORD 272 | lightMapColor = texture2D(m_LightMap, texCoord2).rgb; 273 | #else 274 | lightMapColor = texture2D(m_LightMap, texCoord).rgb; 275 | #endif 276 | specularColor.rgb *= lightMapColor; 277 | diffuseColor.rgb *= lightMapColor; 278 | #endif 279 | 280 | #ifdef VERTEX_LIGHTING 281 | vec2 light = vertexLightValues.xy; 282 | #ifdef COLORRAMP 283 | light.x = texture2D(m_ColorRamp, vec2(light.x, 0.0)).r; 284 | light.y = texture2D(m_ColorRamp, vec2(light.y, 0.0)).r; 285 | #endif 286 | 287 | gl_FragColor.rgb = AmbientSum * diffuseColor.rgb + 288 | DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) + 289 | SpecularSum * specularColor.rgb * vec3(light.y); 290 | #else 291 | vec4 lightDir = vLightDir; 292 | lightDir.xyz = normalize(lightDir.xyz); 293 | vec3 viewDir = normalize(vViewDir); 294 | 295 | vec2 light = computeLighting(normal, viewDir, lightDir.xyz) * spotFallOff; 296 | #ifdef COLORRAMP 297 | diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb; 298 | specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb; 299 | #endif 300 | 301 | // Workaround, since it is not possible to modify varying variables 302 | vec4 SpecularSum2 = vec4(SpecularSum, 1.0); 303 | #ifdef USE_REFLECTION 304 | vec4 refColor = Optics_GetEnvColor(m_EnvMap, refVec.xyz); 305 | 306 | // Interpolate light specularity toward reflection color 307 | // Multiply result by specular map 308 | specularColor = mix(SpecularSum2 * light.y, refColor, refVec.w) * specularColor; 309 | 310 | SpecularSum2 = vec4(1.0); 311 | light.y = 1.0; 312 | #endif 313 | 314 | gl_FragColor.rgb = AmbientSum * diffuseColor.rgb + 315 | DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) + 316 | SpecularSum2.rgb * specularColor.rgb * vec3(light.y); 317 | #endif 318 | gl_FragColor.a = alpha; 319 | } 320 | -------------------------------------------------------------------------------- /assets/MatDefs/Grass.j3md: -------------------------------------------------------------------------------- 1 | MaterialDef Grass Blades { 2 | 3 | MaterialParameters { 4 | 5 | // Compute vertex lighting in the shader 6 | // For better performance 7 | Boolean VertexLighting 8 | 9 | // Use more efficent algorithms to improve performance 10 | Boolean LowQuality 11 | 12 | // Improve quality at the cost of performance 13 | Boolean HighQuality 14 | 15 | // Output alpha from the diffuse map 16 | Boolean UseAlpha 17 | 18 | // Alpha threshold for fragment discarding 19 | Float AlphaDiscardThreshold (AlphaTestFallOff) 20 | 21 | // Normal map is in BC5/ATI2n/LATC/3Dc compression format 22 | Boolean LATC 23 | 24 | // Use the provided ambient, diffuse, and specular colors 25 | Boolean UseMaterialColors 26 | 27 | // Activate shading along the tangent, instead of the normal 28 | // Requires tangent data to be available on the model. 29 | Boolean VTangent 30 | 31 | // Use minnaert diffuse instead of lambert 32 | Boolean Minnaert 33 | 34 | // Use ward specular instead of phong 35 | Boolean WardIso 36 | 37 | // Use vertex color as an additional diffuse color. 38 | Boolean UseVertexColor 39 | 40 | // Ambient color 41 | Color Ambient (MaterialAmbient) 42 | 43 | // Diffuse color 44 | Color Diffuse (MaterialDiffuse) 45 | 46 | // Specular color 47 | Color Specular (MaterialSpecular) 48 | 49 | // Specular power/shininess 50 | Float Shininess (MaterialShininess) : 1 51 | 52 | // Diffuse map 53 | Texture2D DiffuseMap 54 | 55 | // Normal map 56 | Texture2D NormalMap 57 | 58 | // Specular/gloss map 59 | Texture2D SpecularMap 60 | 61 | // Parallax/height map 62 | Texture2D ParallaxMap 63 | 64 | //Set to true is parallax map is stored in the alpha channel of the normal map 65 | Boolean PackedNormalParallax 66 | 67 | //Sets the relief height for parallax mapping 68 | Float ParallaxHeight : 0.05 69 | 70 | //Set to true to activate Steep Parallax mapping 71 | Boolean SteepParallax 72 | 73 | // Texture that specifies alpha values 74 | Texture2D AlphaMap 75 | 76 | // Color ramp, will map diffuse and specular values through it. 77 | Texture2D ColorRamp 78 | 79 | // Texture of the glowing parts of the material 80 | Texture2D GlowMap 81 | 82 | // Set to Use Lightmap 83 | Texture2D LightMap 84 | 85 | // Set to use TexCoord2 for the lightmap sampling 86 | Boolean SeparateTexCoord 87 | 88 | // The glow color of the object 89 | Color GlowColor 90 | 91 | // Parameters for fresnel 92 | // X = bias 93 | // Y = scale 94 | // Z = power 95 | Vector3 FresnelParams 96 | 97 | // Env Map for reflection 98 | TextureCubeMap EnvMap 99 | 100 | // the env map is a spheremap and not a cube map 101 | Boolean EnvMapAsSphereMap 102 | 103 | //shadows 104 | Int FilterMode 105 | Boolean HardwareShadows 106 | 107 | Texture2D ShadowMap0 108 | Texture2D ShadowMap1 109 | Texture2D ShadowMap2 110 | Texture2D ShadowMap3 111 | //pointLights 112 | Texture2D ShadowMap4 113 | Texture2D ShadowMap5 114 | 115 | Float ShadowIntensity 116 | Vector4 Splits 117 | Vector2 FadeInfo 118 | 119 | Matrix4 LightViewProjectionMatrix0 120 | Matrix4 LightViewProjectionMatrix1 121 | Matrix4 LightViewProjectionMatrix2 122 | Matrix4 LightViewProjectionMatrix3 123 | //pointLight 124 | Matrix4 LightViewProjectionMatrix4 125 | Matrix4 LightViewProjectionMatrix5 126 | Vector3 LightPos 127 | 128 | Float PCFEdge 129 | Float ShadowMapSize 130 | 131 | // For hardware skinning 132 | Int NumberOfBones 133 | Matrix4Array BoneMatrices 134 | 135 | 136 | // Grass parameters 137 | Float DistanceFalloff : 48 138 | Texture2D Noise 139 | Vector3 WorldOffset 140 | 141 | Boolean UseWind : true 142 | Boolean UseDiscard : true 143 | Boolean UseDarkening : true 144 | Boolean UseTaper : true 145 | } 146 | 147 | Technique { 148 | 149 | LightMode MultiPass 150 | 151 | VertexShader GLSL110: MatDefs/Grass.vert 152 | FragmentShader GLSL110: MatDefs/Grass.frag 153 | 154 | WorldParameters { 155 | WorldViewProjectionMatrix 156 | ViewProjectionMatrix 157 | NormalMatrix 158 | WorldViewMatrix 159 | ViewMatrix 160 | CameraPosition 161 | WorldMatrix 162 | Time 163 | } 164 | 165 | Defines { 166 | LATC : LATC 167 | VERTEX_COLOR : UseVertexColor 168 | VERTEX_LIGHTING : VertexLighting 169 | ATTENUATION : Attenuation 170 | MATERIAL_COLORS : UseMaterialColors 171 | V_TANGENT : VTangent 172 | MINNAERT : Minnaert 173 | WARDISO : WardIso 174 | LOW_QUALITY : LowQuality 175 | HQ_ATTENUATION : HighQuality 176 | 177 | DIFFUSEMAP : DiffuseMap 178 | NORMALMAP : NormalMap 179 | SPECULARMAP : SpecularMap 180 | PARALLAXMAP : ParallaxMap 181 | NORMALMAP_PARALLAX : PackedNormalParallax 182 | STEEP_PARALLAX : SteepParallax 183 | ALPHAMAP : AlphaMap 184 | COLORRAMP : ColorRamp 185 | LIGHTMAP : LightMap 186 | SEPARATE_TEXCOORD : SeparateTexCoord 187 | 188 | USE_REFLECTION : EnvMap 189 | SPHERE_MAP : SphereMap 190 | 191 | NUM_BONES : NumberOfBones 192 | 193 | USE_WIND : UseWind 194 | USE_DISCARD : UseDiscard 195 | USE_DARKENING : UseDarkening 196 | USE_TAPER : UseTaper 197 | } 198 | } 199 | 200 | Technique PreShadow { 201 | 202 | VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert 203 | FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag 204 | 205 | WorldParameters { 206 | WorldViewProjectionMatrix 207 | WorldViewMatrix 208 | } 209 | 210 | Defines { 211 | COLOR_MAP : ColorMap 212 | DISCARD_ALPHA : AlphaDiscardThreshold 213 | NUM_BONES : NumberOfBones 214 | } 215 | 216 | ForcedRenderState { 217 | FaceCull Off 218 | DepthTest On 219 | DepthWrite On 220 | PolyOffset 5 3 221 | ColorWrite Off 222 | } 223 | 224 | } 225 | 226 | 227 | Technique PostShadow15{ 228 | VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert 229 | FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag 230 | 231 | WorldParameters { 232 | WorldViewProjectionMatrix 233 | WorldMatrix 234 | } 235 | 236 | Defines { 237 | HARDWARE_SHADOWS : HardwareShadows 238 | FILTER_MODE : FilterMode 239 | PCFEDGE : PCFEdge 240 | DISCARD_ALPHA : AlphaDiscardThreshold 241 | COLOR_MAP : ColorMap 242 | SHADOWMAP_SIZE : ShadowMapSize 243 | FADE : FadeInfo 244 | PSSM : Splits 245 | POINTLIGHT : LightViewProjectionMatrix5 246 | NUM_BONES : NumberOfBones 247 | } 248 | 249 | ForcedRenderState { 250 | Blend Modulate 251 | DepthWrite Off 252 | PolyOffset -0.1 0 253 | } 254 | } 255 | 256 | Technique PostShadow{ 257 | VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert 258 | FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag 259 | 260 | WorldParameters { 261 | WorldViewProjectionMatrix 262 | WorldMatrix 263 | } 264 | 265 | Defines { 266 | HARDWARE_SHADOWS : HardwareShadows 267 | FILTER_MODE : FilterMode 268 | PCFEDGE : PCFEdge 269 | DISCARD_ALPHA : AlphaDiscardThreshold 270 | COLOR_MAP : ColorMap 271 | SHADOWMAP_SIZE : ShadowMapSize 272 | FADE : FadeInfo 273 | PSSM : Splits 274 | POINTLIGHT : LightViewProjectionMatrix5 275 | NUM_BONES : NumberOfBones 276 | } 277 | 278 | ForcedRenderState { 279 | Blend Modulate 280 | DepthWrite Off 281 | PolyOffset -0.1 0 282 | } 283 | } 284 | 285 | Technique PreNormalPass { 286 | 287 | VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert 288 | FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag 289 | 290 | WorldParameters { 291 | WorldViewProjectionMatrix 292 | WorldViewMatrix 293 | NormalMatrix 294 | } 295 | 296 | Defines { 297 | DIFFUSEMAP_ALPHA : DiffuseMap 298 | NUM_BONES : NumberOfBones 299 | } 300 | 301 | } 302 | 303 | Technique GBuf { 304 | 305 | VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert 306 | FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag 307 | 308 | WorldParameters { 309 | WorldViewProjectionMatrix 310 | NormalMatrix 311 | WorldViewMatrix 312 | WorldMatrix 313 | } 314 | 315 | Defines { 316 | VERTEX_COLOR : UseVertexColor 317 | MATERIAL_COLORS : UseMaterialColors 318 | V_TANGENT : VTangent 319 | MINNAERT : Minnaert 320 | WARDISO : WardIso 321 | 322 | DIFFUSEMAP : DiffuseMap 323 | NORMALMAP : NormalMap 324 | SPECULARMAP : SpecularMap 325 | PARALLAXMAP : ParallaxMap 326 | } 327 | } 328 | 329 | Technique { 330 | LightMode FixedPipeline 331 | } 332 | 333 | Technique Glow { 334 | 335 | VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert 336 | FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag 337 | 338 | WorldParameters { 339 | WorldViewProjectionMatrix 340 | } 341 | 342 | Defines { 343 | NEED_TEXCOORD1 344 | HAS_GLOWMAP : GlowMap 345 | HAS_GLOWCOLOR : GlowColor 346 | 347 | NUM_BONES : NumberOfBones 348 | } 349 | } 350 | 351 | } 352 | -------------------------------------------------------------------------------- /assets/MatDefs/Grass.vert: -------------------------------------------------------------------------------- 1 | #define ATTENUATION 2 | //#define HQ_ATTENUATION 3 | 4 | #import "Common/ShaderLib/Skinning.glsllib" 5 | 6 | uniform mat4 g_WorldViewProjectionMatrix; 7 | uniform mat4 g_ViewProjectionMatrix; 8 | uniform mat4 g_WorldViewMatrix; 9 | uniform mat4 g_WorldMatrix; 10 | uniform mat3 g_NormalMatrix; 11 | uniform mat4 g_ViewMatrix; 12 | uniform vec3 g_CameraPosition; 13 | uniform float g_Time; 14 | 15 | uniform vec4 m_Ambient; 16 | uniform vec4 m_Diffuse; 17 | uniform vec4 m_Specular; 18 | uniform float m_Shininess; 19 | 20 | uniform vec4 g_LightColor; 21 | uniform vec4 g_LightPosition; 22 | uniform vec4 g_AmbientLightColor; 23 | 24 | varying vec2 texCoord; 25 | #ifdef SEPARATE_TEXCOORD 26 | varying vec2 texCoord2; 27 | attribute vec2 inTexCoord2; 28 | #endif 29 | 30 | varying vec3 AmbientSum; 31 | varying vec4 DiffuseSum; 32 | varying vec3 SpecularSum; 33 | 34 | varying float vDistance; 35 | uniform sampler2D m_Noise; 36 | uniform vec3 m_WorldOffset; 37 | 38 | attribute vec3 inPosition; 39 | attribute vec2 inTexCoord; 40 | attribute vec3 inNormal; 41 | 42 | varying vec3 lightVec; 43 | //varying vec4 spotVec; 44 | 45 | #ifdef VERTEX_COLOR 46 | attribute vec4 inColor; 47 | #endif 48 | 49 | #ifndef VERTEX_LIGHTING 50 | attribute vec4 inTangent; 51 | 52 | #ifndef NORMALMAP 53 | varying vec3 vNormal; 54 | #endif 55 | //varying vec3 vPosition; 56 | varying vec3 vViewDir; 57 | varying vec4 vLightDir; 58 | #else 59 | varying vec2 vertexLightValues; 60 | uniform vec4 g_LightDirection; 61 | #endif 62 | 63 | #ifdef USE_REFLECTION 64 | uniform vec3 g_CameraPosition; 65 | uniform mat4 g_WorldMatrix; 66 | 67 | uniform vec3 m_FresnelParams; 68 | varying vec4 refVec; 69 | 70 | 71 | /** 72 | * Input: 73 | * attribute inPosition 74 | * attribute inNormal 75 | * uniform g_WorldMatrix 76 | * uniform g_CameraPosition 77 | * 78 | * Output: 79 | * varying refVec 80 | */ 81 | void computeRef(in vec4 modelSpacePos){ 82 | vec3 worldPos = (g_WorldMatrix * modelSpacePos).xyz; 83 | 84 | vec3 I = normalize( g_CameraPosition - worldPos ).xyz; 85 | vec3 N = normalize( (g_WorldMatrix * vec4(inNormal, 0.0)).xyz ); 86 | 87 | refVec.xyz = reflect(I, N); 88 | refVec.w = m_FresnelParams.x + m_FresnelParams.y * pow(1.0 + dot(I, N), m_FresnelParams.z); 89 | } 90 | #endif 91 | 92 | // JME3 lights in world space 93 | void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){ 94 | float posLight = step(0.5, color.w); 95 | vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight); 96 | lightVec = tempVec; 97 | #ifdef ATTENUATION 98 | float dist = length(tempVec); 99 | lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0); 100 | lightDir.xyz = tempVec / vec3(dist); 101 | #else 102 | lightDir = vec4(normalize(tempVec), 1.0); 103 | #endif 104 | } 105 | 106 | #ifdef VERTEX_LIGHTING 107 | float lightComputeDiffuse(in vec3 norm, in vec3 lightdir){ 108 | return max(0.0, dot(norm, lightdir)); 109 | } 110 | 111 | float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){ 112 | if (shiny <= 1.0){ 113 | return 0.0; 114 | } 115 | #ifndef LOW_QUALITY 116 | vec3 H = (viewdir + lightdir) * vec3(0.5); 117 | return pow(max(dot(H, norm), 0.0), shiny); 118 | #else 119 | return 0.0; 120 | #endif 121 | } 122 | 123 | vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){ 124 | vec4 lightDir; 125 | lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir); 126 | float spotFallOff = 1.0; 127 | if(g_LightDirection.w != 0.0){ 128 | vec3 L=normalize(lightVec.xyz); 129 | vec3 spotdir = normalize(g_LightDirection.xyz); 130 | float curAngleCos = dot(-L, spotdir); 131 | float innerAngleCos = floor(g_LightDirection.w) * 0.001; 132 | float outerAngleCos = fract(g_LightDirection.w); 133 | float innerMinusOuter = innerAngleCos - outerAngleCos; 134 | spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0); 135 | } 136 | float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz); 137 | float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess); 138 | //specularFactor *= step(0.01, diffuseFactor); 139 | return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w)*spotFallOff; 140 | } 141 | #endif 142 | 143 | void main(){ 144 | vec4 modelSpacePos = vec4(inPosition, 1.0); 145 | vec3 modelSpaceNorm = inNormal; 146 | 147 | #ifndef VERTEX_LIGHTING 148 | vec3 modelSpaceTan = inTangent.xyz; 149 | #endif 150 | 151 | #ifdef NUM_BONES 152 | #ifndef VERTEX_LIGHTING 153 | Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan); 154 | #else 155 | Skinning_Compute(modelSpacePos, modelSpaceNorm); 156 | #endif 157 | #endif 158 | 159 | // We plot the position of the grass vertexes based on the 160 | // texture coordinates 161 | //gl_Position = g_WorldViewProjectionMatrix * modelSpacePos; 162 | texCoord = inTexCoord; 163 | #ifdef SEPARATE_TEXCOORD 164 | texCoord2 = inTexCoord2; 165 | #endif 166 | 167 | // Find the world location of the vertex. All three corners 168 | // will have the same vertex. 169 | vec3 wPos = (g_WorldMatrix * modelSpacePos).xyz; 170 | vec2 groundPos = wPos.xz + m_WorldOffset.xz; 171 | 172 | // We face the billboarded grass towards the camera's location 173 | // instead of parallel to the screen. This keeps the blades from 174 | // sliding around if we turn the camera. 175 | vec3 cameraOffset = wPos - g_CameraPosition; 176 | vDistance = length(cameraOffset); 177 | vec3 cameraDir = cameraOffset / vDistance; 178 | vec3 posOffset = normalize(vec3(-cameraDir.z, 0.0, cameraDir.x)); 179 | 180 | // The whole part of the x coordinate is the atlas cell. 181 | // The fractional part says which corner this is. 182 | // X fract() will be 0.25, 0.5, or 0.0 183 | // Y will be 1 at x=0 and x=0.5 but 0 at x=0.25. 184 | // I kept the decimal part small so that it could be safely 185 | // extracted from the texture coordinate. 186 | float texFract = fract(texCoord.x); 187 | float offsetLength = (texFract * 2.0) - 0.5; 188 | float texY = abs(offsetLength) * 2.0; 189 | float normalProjectionLength = texY - 0.25; 190 | float size = texCoord.y; 191 | 192 | modelSpacePos.xyz += modelSpaceNorm * normalProjectionLength * size; 193 | wPos = (g_WorldMatrix * modelSpacePos).xyz; 194 | 195 | // Move the upper parts of the triangle along the camera-perpendicular 196 | // vector (posOffset) 197 | wPos += posOffset * offsetLength * size; 198 | 199 | #ifdef USE_WIND 200 | // some simple wind 201 | vec4 noise = texture2D(m_Noise, vec2(groundPos.x * 0.01 + g_Time * 0.01, groundPos.y * 0.01)); 202 | //wPos.x += (noise.x * 0.5 - 0.25) * normalProjectionLength; 203 | float strength = noise.y * 0.15 * size; 204 | wPos.x += sin(g_Time * (1.0 + noise.x)) * normalProjectionLength * strength; 205 | #endif 206 | 207 | gl_Position = g_ViewProjectionMatrix * vec4(wPos, 1.0); 208 | 209 | // Figure out the texture coordinate from the index 210 | float index = texCoord.x - texFract; 211 | float u = mod(index, 4.0); 212 | float v = mod((index - u) * 0.25, 4.0); 213 | texCoord.x = u * 0.25 + texFract * 0.5; 214 | texCoord.y = v * 0.25 + texY * 0.25; 215 | 216 | 217 | vec3 wvPosition = (g_WorldViewMatrix * modelSpacePos).xyz; 218 | vec3 wvNormal = normalize(g_NormalMatrix * modelSpaceNorm); 219 | vec3 viewDir = normalize(-wvPosition); 220 | 221 | //vec4 lightColor = g_LightColor[gl_InstanceID]; 222 | //vec4 lightPos = g_LightPosition[gl_InstanceID]; 223 | //vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w)); 224 | //wvLightPos.w = lightPos.w; 225 | 226 | vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0))); 227 | wvLightPos.w = g_LightPosition.w; 228 | vec4 lightColor = g_LightColor; 229 | 230 | #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) 231 | vec3 wvTangent = normalize(g_NormalMatrix * modelSpaceTan); 232 | vec3 wvBinormal = cross(wvNormal, wvTangent); 233 | 234 | mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal); 235 | 236 | //vPosition = wvPosition * tbnMat; 237 | //vViewDir = viewDir * tbnMat; 238 | vViewDir = -wvPosition * tbnMat; 239 | lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir); 240 | vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz; 241 | #elif !defined(VERTEX_LIGHTING) 242 | vNormal = wvNormal; 243 | 244 | //vPosition = wvPosition; 245 | vViewDir = viewDir; 246 | 247 | lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir); 248 | 249 | #ifdef V_TANGENT 250 | vNormal = normalize(g_NormalMatrix * inTangent.xyz); 251 | vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal); 252 | #endif 253 | #endif 254 | 255 | //computing spot direction in view space and unpacking spotlight cos 256 | // spotVec = (g_ViewMatrix * vec4(g_LightDirection.xyz, 0.0) ); 257 | // spotVec.w = floor(g_LightDirection.w) * 0.001; 258 | // lightVec.w = fract(g_LightDirection.w); 259 | 260 | lightColor.w = 1.0; 261 | #ifdef MATERIAL_COLORS 262 | AmbientSum = (m_Ambient * g_AmbientLightColor).rgb; 263 | DiffuseSum = m_Diffuse * lightColor; 264 | SpecularSum = (m_Specular * lightColor).rgb; 265 | #else 266 | AmbientSum = vec3(0.2, 0.2, 0.2) * g_AmbientLightColor.rgb; // Default: ambient color is dark gray 267 | DiffuseSum = lightColor; 268 | SpecularSum = vec3(0.0); 269 | #endif 270 | 271 | #ifdef VERTEX_COLOR 272 | AmbientSum *= inColor.rgb; 273 | DiffuseSum *= inColor; 274 | #endif 275 | 276 | #ifdef VERTEX_LIGHTING 277 | vertexLightValues = computeLighting(wvPosition, wvNormal, viewDir, wvLightPos); 278 | #endif 279 | 280 | #ifdef USE_REFLECTION 281 | computeRef(modelSpacePos); 282 | #endif 283 | } 284 | -------------------------------------------------------------------------------- /assets/MatDefs/TrilinearLighting.frag: -------------------------------------------------------------------------------- 1 | #import "Common/ShaderLib/Parallax.glsllib" 2 | #import "Common/ShaderLib/Optics.glsllib" 3 | #define ATTENUATION 4 | //#define HQ_ATTENUATION 5 | 6 | #import "MatDefs/FragScattering.glsllib" 7 | 8 | // Trinlinear mapping related stuff 9 | uniform mat3 g_NormalMatrix; 10 | varying vec3 worldNormal; 11 | 12 | varying vec3 worldTangent; 13 | 14 | varying float z; 15 | uniform sampler2D m_Noise; 16 | 17 | uniform float m_LowResDistance; 18 | uniform sampler2D m_DiffuseMapLow; 19 | uniform sampler2D m_DiffuseMapX; 20 | uniform sampler2D m_NormalMapX; 21 | uniform sampler2D m_DiffuseMapY; 22 | uniform sampler2D m_NormalMapY; 23 | uniform sampler2D m_DiffuseMapZ; 24 | uniform sampler2D m_NormalMapZ; 25 | 26 | 27 | varying vec3 texCoord; 28 | #ifdef SEPARATE_TEXCOORD 29 | varying vec2 texCoord2; 30 | #endif 31 | 32 | varying vec3 AmbientSum; 33 | varying vec4 DiffuseSum; 34 | varying vec3 SpecularSum; 35 | 36 | #ifndef VERTEX_LIGHTING 37 | uniform vec4 g_LightDirection; 38 | //varying vec3 vPosition; 39 | varying vec3 vViewDir; 40 | varying vec4 vLightDir; 41 | varying vec3 lightVec; 42 | #else 43 | varying vec2 vertexLightValues; 44 | #endif 45 | 46 | #ifdef DIFFUSEMAP 47 | uniform sampler2D m_DiffuseMap; 48 | #endif 49 | 50 | #ifdef SPECULARMAP 51 | uniform sampler2D m_SpecularMap; 52 | #endif 53 | 54 | #ifdef PARALLAXMAP 55 | uniform sampler2D m_ParallaxMap; 56 | #endif 57 | #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 58 | uniform float m_ParallaxHeight; 59 | #endif 60 | 61 | #ifdef LIGHTMAP 62 | uniform sampler2D m_LightMap; 63 | #endif 64 | 65 | #ifdef NORMALMAP 66 | uniform sampler2D m_NormalMap; 67 | 68 | // For debugging we want it either way 69 | varying vec3 vNormal; 70 | #else 71 | varying vec3 vNormal; 72 | #endif 73 | 74 | #ifdef ALPHAMAP 75 | uniform sampler2D m_AlphaMap; 76 | #endif 77 | 78 | #ifdef COLORRAMP 79 | uniform sampler2D m_ColorRamp; 80 | #endif 81 | 82 | uniform float m_AlphaDiscardThreshold; 83 | 84 | #ifndef VERTEX_LIGHTING 85 | uniform float m_Shininess; 86 | 87 | #ifdef HQ_ATTENUATION 88 | uniform vec4 g_LightPosition; 89 | #endif 90 | 91 | #ifdef USE_REFLECTION 92 | uniform float m_ReflectionPower; 93 | uniform float m_ReflectionIntensity; 94 | varying vec4 refVec; 95 | 96 | uniform ENVMAP m_EnvMap; 97 | #endif 98 | 99 | float tangDot(in vec3 v1, in vec3 v2){ 100 | float d = dot(v1,v2); 101 | #ifdef V_TANGENT 102 | d = 1.0 - d*d; 103 | return step(0.0, d) * sqrt(d); 104 | #else 105 | return d; 106 | #endif 107 | } 108 | 109 | float lightComputeDiffuse(in vec3 norm, in vec3 lightdir, in vec3 viewdir){ 110 | #ifdef MINNAERT 111 | float NdotL = max(0.0, dot(norm, lightdir)); 112 | float NdotV = max(0.0, dot(norm, viewdir)); 113 | return NdotL * pow(max(NdotL * NdotV, 0.1), -1.0) * 0.5; 114 | #else 115 | return max(0.0, dot(norm, lightdir)); 116 | #endif 117 | } 118 | 119 | float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){ 120 | // NOTE: check for shiny <= 1 removed since shininess is now 121 | // 1.0 by default (uses matdefs default vals) 122 | #ifdef LOW_QUALITY 123 | // Blinn-Phong 124 | // Note: preferably, H should be computed in the vertex shader 125 | vec3 H = (viewdir + lightdir) * vec3(0.5); 126 | return pow(max(tangDot(H, norm), 0.0), shiny); 127 | #elif defined(WARDISO) 128 | // Isotropic Ward 129 | vec3 halfVec = normalize(viewdir + lightdir); 130 | float NdotH = max(0.001, tangDot(norm, halfVec)); 131 | float NdotV = max(0.001, tangDot(norm, viewdir)); 132 | float NdotL = max(0.001, tangDot(norm, lightdir)); 133 | float a = tan(acos(NdotH)); 134 | float p = max(shiny/128.0, 0.001); 135 | return NdotL * (1.0 / (4.0*3.14159265*p*p)) * (exp(-(a*a)/(p*p)) / (sqrt(NdotV * NdotL))); 136 | #else 137 | // Standard Phong 138 | vec3 R = reflect(-lightdir, norm); 139 | return pow(max(tangDot(R, viewdir), 0.0), shiny); 140 | #endif 141 | } 142 | 143 | vec2 computeLighting(in vec3 wvNorm, in vec3 wvViewDir, in vec3 wvLightDir){ 144 | float diffuseFactor = lightComputeDiffuse(wvNorm, wvLightDir, wvViewDir); 145 | float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, wvLightDir, m_Shininess); 146 | 147 | #ifdef HQ_ATTENUATION 148 | float att = clamp(1.0 - g_LightPosition.w * length(lightVec), 0.0, 1.0); 149 | #else 150 | float att = vLightDir.w; 151 | #endif 152 | 153 | if (m_Shininess <= 1.0) { 154 | specularFactor = 0.0; // should be one instruction on most cards .. 155 | } 156 | 157 | specularFactor *= diffuseFactor; 158 | 159 | return vec2(diffuseFactor, specularFactor) * vec2(att); 160 | } 161 | #endif 162 | 163 | vec4 getColor( in sampler2D diffuseMap, in sampler2D diffuseMapLow, in sampler2D normalMap, in vec2 tc, in float distMix, out vec3 normal ) { 164 | 165 | vec2 tcOffset; 166 | tcOffset = texture2D(m_Noise, tc * 0.01).xy * 6.0 - 3.0; 167 | vec4 diffuseColor = texture2D(diffuseMap, (tc + tcOffset) * 0.75); 168 | 169 | tcOffset.x = (texture2D(m_Noise, tc * 0.01).x * 0.2) - 0.1; 170 | tcOffset.y = (texture2D(m_Noise, tc * 0.03).x * 0.2) - 0.1; 171 | vec4 subColor = texture2D(diffuseMapLow, tc * 0.1 + tcOffset * 0.1); 172 | diffuseColor = mix(diffuseColor, subColor, distMix); 173 | 174 | vec4 normalHeight = texture2D(normalMap, tc); 175 | normal = normalize((normalHeight.xyz * vec3(2.0) - vec3(1.0))); 176 | 177 | return diffuseColor; 178 | } 179 | 180 | 181 | void main(){ 182 | vec2 newTexCoord; 183 | 184 | /**** 185 | This is taken care of by the trilinear mapping now 186 | #if (defined(PARALLAXMAP) || (defined(NORMALMAP_PARALLAX) && defined(NORMALMAP))) && !defined(VERTEX_LIGHTING) 187 | 188 | #ifdef STEEP_PARALLAX 189 | #ifdef NORMALMAP_PARALLAX 190 | //parallax map is stored in the alpha channel of the normal map 191 | newTexCoord = steepParallaxOffset(m_NormalMap, vViewDir, texCoord.xz, m_ParallaxHeight); 192 | #else 193 | //parallax map is a texture 194 | newTexCoord = steepParallaxOffset(m_ParallaxMap, vViewDir, texCoord.xz, m_ParallaxHeight); 195 | #endif 196 | #else 197 | #ifdef NORMALMAP_PARALLAX 198 | //parallax map is stored in the alpha channel of the normal map 199 | newTexCoord = classicParallaxOffset(m_NormalMap, vViewDir, texCoord.xz, m_ParallaxHeight); 200 | #else 201 | //parallax map is a texture 202 | newTexCoord = classicParallaxOffset(m_ParallaxMap, vViewDir, texCoord.xz, m_ParallaxHeight); 203 | #endif 204 | #endif 205 | #else 206 | newTexCoord = texCoord.xz; 207 | #endif 208 | 209 | #ifdef DIFFUSEMAP 210 | vec4 diffuseColor = texture2D(m_DiffuseMap, newTexCoord); 211 | #else 212 | vec4 diffuseColor = vec4(1.0); 213 | #endif 214 | 215 | float alpha = DiffuseSum.a * diffuseColor.a; 216 | #ifdef ALPHAMAP 217 | alpha = alpha * texture2D(m_AlphaMap, newTexCoord).r; 218 | #endif 219 | if(alpha < m_AlphaDiscardThreshold){ 220 | discard; 221 | } 222 | */ 223 | 224 | //diffuseColor.xyz = normalize(worldNormal); 225 | float alpha = 1.0; 226 | 227 | /***** 228 | This is taken care of by the trilinear mapping now 229 | // *********************** 230 | // Read from textures 231 | // *********************** 232 | #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) 233 | vec4 normalHeight = texture2D(m_NormalMap, newTexCoord); 234 | vec3 normal = normalize((normalHeight.xyz * vec3(2.0) - vec3(1.0))); 235 | #ifdef LATC 236 | normal.z = sqrt(1.0 - (normal.x * normal.x) - (normal.y * normal.y)); 237 | #endif 238 | //normal.y = -normal.y; 239 | #elif !defined(VERTEX_LIGHTING) 240 | vec3 normal = vNormal; 241 | #if !defined(LOW_QUALITY) && !defined(V_TANGENT) 242 | normal = normalize(normal); 243 | #endif 244 | #endif 245 | */ 246 | 247 | 248 | // ************************************* 249 | // Trinlinear Mapping 250 | // ************************************* 251 | 252 | // We change the low res to hi res based on distance 253 | float lowMix = z / m_LowResDistance; 254 | lowMix = clamp(lowMix, 0.5, 1.0); 255 | 256 | // Collect the basic axis textures for x, y, and z 257 | vec3 normalX; 258 | vec3 normalY; 259 | vec3 normalZ; 260 | vec3 normalTop; 261 | 262 | vec4 xColor = getColor(m_DiffuseMapX, m_DiffuseMapX, m_NormalMapX, texCoord.zy, lowMix, normalX); 263 | vec4 yColor = getColor(m_DiffuseMapY, m_DiffuseMapY, m_NormalMapY, texCoord.xz, lowMix, normalY); 264 | vec4 zColor = getColor(m_DiffuseMapZ, m_DiffuseMapZ, m_NormalMapZ, texCoord.xy, lowMix, normalZ); 265 | 266 | // Turn the normal into the blending ratios. 267 | float up = worldNormal.y; 268 | float offset = texture2D(m_Noise, texCoord.xz).x; 269 | 270 | // We bias the threshold a bit based on a noise value. 271 | float threshold = up + (offset * 0.1 - 0.05); 272 | 273 | // Now bias the y blend factor by the threshold if it exceeds 274 | // 45 degrees, basically. The noise above will give us some 275 | // nice rough edges. 276 | vec3 blend = abs(normalize(worldNormal)); 277 | if( threshold > 0.707 ) { 278 | blend.y += 10.0 * offset; 279 | } 280 | 281 | // "normalize" it once with the initial bias in place. 282 | // blend should always add to 1.0 283 | blend /= (blend.x + blend.y + blend.z); 284 | 285 | // The top will use a different texture based on the threshold so far 286 | vec4 topColor = getColor(m_DiffuseMap, m_DiffuseMapLow, m_NormalMapY, texCoord.xz, lowMix, normalTop); 287 | 288 | // Mix a border into the topColor based on an area around the threshold 289 | float edge1 = smoothstep(0.5, 0.72, threshold); 290 | float edge2 = 1.0 - smoothstep(0.72, 0.75, threshold); 291 | topColor = mix(topColor, topColor * vec4(0.6, 0.5, 0.5, 1.0), edge1 * edge2); 292 | 293 | // Top will be 1.0 if normal is up, 0 otherwise 294 | float top = step(worldNormal.y, 0.0); 295 | 296 | // Select the top or the bottom texture based on sign of y 297 | // ...and darken the bottom texture while we are at it 298 | yColor = topColor * (1.0 - top) + yColor * top * 0.5; 299 | 300 | // Bias the y blend value based on the up-ness and 301 | // the edges 302 | blend.y *= max(top, edge1 * edge1); 303 | 304 | // It is important that x, y, z add up to just 1.0 and only 1.0 305 | blend /= (blend.x + blend.y + blend.z); 306 | 307 | // If we are very close to the grass edge then also darken the 308 | // other two axes just a bit. 309 | float darken = min(1.0, 0.9 + (1.0 - edge1) * 0.1); 310 | 311 | vec4 diffuseColor = xColor * blend.x * darken 312 | + yColor * blend.y 313 | + zColor * blend.z * darken; 314 | 315 | // Move the normal map normals into their respective axis world space 316 | // a bit arbitrarily. 317 | normalX = vec3(0.0, -normalX.y, normalX.x); 318 | normalY = vec3(normalY.x, 0.0, normalY.y); 319 | normalZ = vec3(normalZ.x, -normalZ.y, 0.0); 320 | 321 | // Mix the normal map normals together based on blend 322 | vec3 bumpNormal = normalX * blend.x 323 | + normalY * blend.y 324 | + normalZ * blend.z; 325 | vec3 normal = normalize(bumpNormal); 326 | 327 | normal = normalize(vNormal + g_NormalMatrix * bumpNormal); 328 | 329 | // Moved this to after trilinear mapping is performed so that the color 330 | // will be accurate 331 | #ifndef VERTEX_LIGHTING 332 | float spotFallOff = 1.0; 333 | 334 | #if __VERSION__ >= 110 335 | // allow use of control flow 336 | if(g_LightDirection.w != 0.0){ 337 | #endif 338 | 339 | vec3 L = normalize(lightVec.xyz); 340 | vec3 spotdir = normalize(g_LightDirection.xyz); 341 | float curAngleCos = dot(-L, spotdir); 342 | float innerAngleCos = floor(g_LightDirection.w) * 0.001; 343 | float outerAngleCos = fract(g_LightDirection.w); 344 | float innerMinusOuter = innerAngleCos - outerAngleCos; 345 | spotFallOff = (curAngleCos - outerAngleCos) / innerMinusOuter; 346 | 347 | #if __VERSION__ >= 110 348 | if(spotFallOff <= 0.0){ 349 | gl_FragColor.rgb = AmbientSum * diffuseColor.rgb; 350 | gl_FragColor.a = alpha; 351 | return; 352 | }else{ 353 | spotFallOff = clamp(spotFallOff, 0.0, 1.0); 354 | } 355 | } 356 | #else 357 | spotFallOff = clamp(spotFallOff, step(g_LightDirection.w, 0.001), 1.0); 358 | #endif 359 | #endif 360 | 361 | #ifdef SPECULARMAP 362 | vec4 specularColor = texture2D(m_SpecularMap, newTexCoord); 363 | #else 364 | vec4 specularColor = vec4(1.0); 365 | #endif 366 | 367 | #ifdef LIGHTMAP 368 | vec3 lightMapColor; 369 | #ifdef SEPARATE_TEXCOORD 370 | lightMapColor = texture2D(m_LightMap, texCoord2).rgb; 371 | #else 372 | lightMapColor = texture2D(m_LightMap, texCoord.xz).rgb; 373 | #endif 374 | specularColor.rgb *= lightMapColor; 375 | diffuseColor.rgb *= lightMapColor; 376 | #endif 377 | 378 | #ifdef VERTEX_LIGHTING 379 | vec2 light = vertexLightValues.xy; 380 | #ifdef COLORRAMP 381 | light.x = texture2D(m_ColorRamp, vec2(light.x, 0.0)).r; 382 | light.y = texture2D(m_ColorRamp, vec2(light.y, 0.0)).r; 383 | #endif 384 | 385 | #ifndef USE_SCATTERING 386 | gl_FragColor.rgb = AmbientSum * diffuseColor.rgb + 387 | DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) + 388 | SpecularSum * specularColor.rgb * vec3(light.y); 389 | #else 390 | vec3 color = AmbientSum * diffuseColor.rgb + 391 | DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) + 392 | SpecularSum * specularColor.rgb * vec3(light.y); 393 | gl_FragColor.rgb = calculateGroundColor(vec4(color, 1.0)).rgb; 394 | #endif 395 | #else 396 | vec4 lightDir = vLightDir; 397 | lightDir.xyz = normalize(lightDir.xyz); 398 | vec3 viewDir = normalize(vViewDir); 399 | 400 | vec2 light = computeLighting(normal, viewDir, lightDir.xyz) * spotFallOff; 401 | #ifdef COLORRAMP 402 | diffuseColor.rgb *= texture2D(m_ColorRamp, vec2(light.x, 0.0)).rgb; 403 | specularColor.rgb *= texture2D(m_ColorRamp, vec2(light.y, 0.0)).rgb; 404 | #endif 405 | 406 | // Workaround, since it is not possible to modify varying variables 407 | vec4 SpecularSum2 = vec4(SpecularSum, 1.0); 408 | #ifdef USE_REFLECTION 409 | vec4 refColor = Optics_GetEnvColor(m_EnvMap, refVec.xyz); 410 | 411 | // Interpolate light specularity toward reflection color 412 | // Multiply result by specular map 413 | specularColor = mix(SpecularSum2 * light.y, refColor, refVec.w) * specularColor; 414 | 415 | SpecularSum2 = vec4(1.0); 416 | light.y = 1.0; 417 | #endif 418 | 419 | #ifndef USE_SCATTERING 420 | gl_FragColor.rgb = AmbientSum * diffuseColor.rgb + 421 | DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) + 422 | SpecularSum * specularColor.rgb * vec3(light.y); 423 | #else 424 | vec3 color = AmbientSum * diffuseColor.rgb + 425 | DiffuseSum.rgb * diffuseColor.rgb * vec3(light.x) + 426 | SpecularSum * specularColor.rgb * vec3(light.y); 427 | gl_FragColor.rgb = calculateGroundColor(vec4(color, 1.0)).rgb; 428 | #endif 429 | #endif 430 | gl_FragColor.a = alpha; 431 | } 432 | -------------------------------------------------------------------------------- /assets/MatDefs/TrilinearLighting.j3md: -------------------------------------------------------------------------------- 1 | MaterialDef Phong Lighting { 2 | 3 | MaterialParameters { 4 | 5 | // Compute vertex lighting in the shader 6 | // For better performance 7 | Boolean VertexLighting 8 | 9 | // Use more efficent algorithms to improve performance 10 | Boolean LowQuality 11 | 12 | // Improve quality at the cost of performance 13 | Boolean HighQuality 14 | 15 | // Output alpha from the diffuse map 16 | Boolean UseAlpha 17 | 18 | // Alpha threshold for fragment discarding 19 | Float AlphaDiscardThreshold (AlphaTestFallOff) 20 | 21 | // Normal map is in BC5/ATI2n/LATC/3Dc compression format 22 | Boolean LATC 23 | 24 | // Use the provided ambient, diffuse, and specular colors 25 | Boolean UseMaterialColors 26 | 27 | // Activate shading along the tangent, instead of the normal 28 | // Requires tangent data to be available on the model. 29 | Boolean VTangent 30 | 31 | // Use minnaert diffuse instead of lambert 32 | Boolean Minnaert 33 | 34 | // Use ward specular instead of phong 35 | Boolean WardIso 36 | 37 | // Use vertex color as an additional diffuse color. 38 | Boolean UseVertexColor 39 | 40 | // Ambient color 41 | Color Ambient (MaterialAmbient) 42 | 43 | // Diffuse color 44 | Color Diffuse (MaterialDiffuse) 45 | 46 | // Specular color 47 | Color Specular (MaterialSpecular) 48 | 49 | // Specular power/shininess 50 | Float Shininess (MaterialShininess) : 1 51 | 52 | // Diffuse map 53 | Texture2D DiffuseMap 54 | 55 | // Normal map 56 | Texture2D NormalMap 57 | 58 | // Specular/gloss map 59 | Texture2D SpecularMap 60 | 61 | // Parallax/height map 62 | Texture2D ParallaxMap 63 | 64 | //Set to true is parallax map is stored in the alpha channel of the normal map 65 | Boolean PackedNormalParallax 66 | 67 | //Sets the relief height for parallax mapping 68 | Float ParallaxHeight : 0.05 69 | 70 | //Set to true to activate Steep Parallax mapping 71 | Boolean SteepParallax 72 | 73 | // Texture that specifies alpha values 74 | Texture2D AlphaMap 75 | 76 | // Color ramp, will map diffuse and specular values through it. 77 | Texture2D ColorRamp 78 | 79 | // Texture of the glowing parts of the material 80 | Texture2D GlowMap 81 | 82 | // Set to Use Lightmap 83 | Texture2D LightMap 84 | 85 | // Set to use TexCoord2 for the lightmap sampling 86 | Boolean SeparateTexCoord 87 | 88 | // The glow color of the object 89 | Color GlowColor 90 | 91 | // Parameters for fresnel 92 | // X = bias 93 | // Y = scale 94 | // Z = power 95 | Vector3 FresnelParams 96 | 97 | // Env Map for reflection 98 | TextureCubeMap EnvMap 99 | 100 | // the env map is a spheremap and not a cube map 101 | Boolean EnvMapAsSphereMap 102 | 103 | //shadows 104 | Int FilterMode 105 | Boolean HardwareShadows 106 | 107 | Texture2D ShadowMap0 108 | Texture2D ShadowMap1 109 | Texture2D ShadowMap2 110 | Texture2D ShadowMap3 111 | //pointLights 112 | Texture2D ShadowMap4 113 | Texture2D ShadowMap5 114 | 115 | Float ShadowIntensity 116 | Vector4 Splits 117 | Vector2 FadeInfo 118 | 119 | Matrix4 LightViewProjectionMatrix0 120 | Matrix4 LightViewProjectionMatrix1 121 | Matrix4 LightViewProjectionMatrix2 122 | Matrix4 LightViewProjectionMatrix3 123 | //pointLight 124 | Matrix4 LightViewProjectionMatrix4 125 | Matrix4 LightViewProjectionMatrix5 126 | Vector3 LightPos 127 | 128 | Float PCFEdge 129 | Float ShadowMapSize 130 | 131 | // For hardware skinning 132 | Int NumberOfBones 133 | Matrix4Array BoneMatrices 134 | 135 | // For the trilinear mapping 136 | Vector3 WorldOffset 137 | 138 | Texture2D DiffuseMapLow 139 | 140 | Texture2D DiffuseMapX 141 | Texture2D DiffuseMapY 142 | Texture2D DiffuseMapZ 143 | 144 | Texture2D NormalMapX 145 | Texture2D NormalMapY 146 | Texture2D NormalMapZ 147 | 148 | Texture2D Noise 149 | Float LowResDistance: 32.0 150 | 151 | // Ground scattering parameters 152 | Boolean UseScattering 153 | Vector3 SunPosition 154 | Float Exposure 155 | Float KmESun 156 | Float InnerRadius 157 | Float RadiusScale 158 | Float PlanetScale : 1 159 | Vector3 InvWavelengthsKrESun 160 | Float AverageDensityScale 161 | Float InvAverageDensityHeight; 162 | Vector3 KWavelengths4PI; 163 | 164 | } 165 | 166 | Technique { 167 | 168 | LightMode MultiPass 169 | 170 | VertexShader GLSL110: MatDefs/TrilinearLighting.vert 171 | FragmentShader GLSL110: MatDefs/TrilinearLighting.frag 172 | 173 | WorldParameters { 174 | WorldViewProjectionMatrix 175 | NormalMatrix 176 | WorldViewMatrix 177 | ViewMatrix 178 | CameraPosition 179 | WorldMatrix 180 | } 181 | 182 | Defines { 183 | LATC : LATC 184 | VERTEX_COLOR : UseVertexColor 185 | VERTEX_LIGHTING : VertexLighting 186 | ATTENUATION : Attenuation 187 | MATERIAL_COLORS : UseMaterialColors 188 | V_TANGENT : VTangent 189 | MINNAERT : Minnaert 190 | WARDISO : WardIso 191 | LOW_QUALITY : LowQuality 192 | HQ_ATTENUATION : HighQuality 193 | 194 | DIFFUSEMAP : DiffuseMap 195 | // For now, we won't set this 196 | // NORMALMAP : NormalMap 197 | SPECULARMAP : SpecularMap 198 | PARALLAXMAP : ParallaxMap 199 | NORMALMAP_PARALLAX : PackedNormalParallax 200 | STEEP_PARALLAX : SteepParallax 201 | ALPHAMAP : AlphaMap 202 | COLORRAMP : ColorRamp 203 | LIGHTMAP : LightMap 204 | SEPARATE_TEXCOORD : SeparateTexCoord 205 | 206 | USE_REFLECTION : EnvMap 207 | SPHERE_MAP : SphereMap 208 | 209 | NUM_BONES : NumberOfBones 210 | 211 | USE_SCATTERING : UseScattering 212 | } 213 | } 214 | 215 | Technique PreShadow { 216 | 217 | VertexShader GLSL100 : Common/MatDefs/Shadow/PreShadow.vert 218 | FragmentShader GLSL100 : Common/MatDefs/Shadow/PreShadow.frag 219 | 220 | WorldParameters { 221 | WorldViewProjectionMatrix 222 | WorldViewMatrix 223 | } 224 | 225 | Defines { 226 | COLOR_MAP : ColorMap 227 | DISCARD_ALPHA : AlphaDiscardThreshold 228 | NUM_BONES : NumberOfBones 229 | } 230 | 231 | ForcedRenderState { 232 | FaceCull Off 233 | DepthTest On 234 | DepthWrite On 235 | PolyOffset 5 3 236 | ColorWrite Off 237 | } 238 | 239 | } 240 | 241 | 242 | Technique PostShadow15{ 243 | VertexShader GLSL150: Common/MatDefs/Shadow/PostShadow15.vert 244 | FragmentShader GLSL150: Common/MatDefs/Shadow/PostShadow15.frag 245 | 246 | WorldParameters { 247 | WorldViewProjectionMatrix 248 | WorldMatrix 249 | } 250 | 251 | Defines { 252 | HARDWARE_SHADOWS : HardwareShadows 253 | FILTER_MODE : FilterMode 254 | PCFEDGE : PCFEdge 255 | DISCARD_ALPHA : AlphaDiscardThreshold 256 | COLOR_MAP : ColorMap 257 | SHADOWMAP_SIZE : ShadowMapSize 258 | FADE : FadeInfo 259 | PSSM : Splits 260 | POINTLIGHT : LightViewProjectionMatrix5 261 | NUM_BONES : NumberOfBones 262 | } 263 | 264 | ForcedRenderState { 265 | Blend Modulate 266 | DepthWrite Off 267 | PolyOffset -0.1 0 268 | } 269 | } 270 | 271 | Technique PostShadow{ 272 | VertexShader GLSL100: Common/MatDefs/Shadow/PostShadow.vert 273 | FragmentShader GLSL100: Common/MatDefs/Shadow/PostShadow.frag 274 | 275 | WorldParameters { 276 | WorldViewProjectionMatrix 277 | WorldMatrix 278 | } 279 | 280 | Defines { 281 | HARDWARE_SHADOWS : HardwareShadows 282 | FILTER_MODE : FilterMode 283 | PCFEDGE : PCFEdge 284 | DISCARD_ALPHA : AlphaDiscardThreshold 285 | COLOR_MAP : ColorMap 286 | SHADOWMAP_SIZE : ShadowMapSize 287 | FADE : FadeInfo 288 | PSSM : Splits 289 | POINTLIGHT : LightViewProjectionMatrix5 290 | NUM_BONES : NumberOfBones 291 | } 292 | 293 | ForcedRenderState { 294 | Blend Modulate 295 | DepthWrite Off 296 | PolyOffset -0.1 0 297 | } 298 | } 299 | 300 | Technique PreNormalPass { 301 | 302 | VertexShader GLSL100 : Common/MatDefs/SSAO/normal.vert 303 | FragmentShader GLSL100 : Common/MatDefs/SSAO/normal.frag 304 | 305 | WorldParameters { 306 | WorldViewProjectionMatrix 307 | WorldViewMatrix 308 | NormalMatrix 309 | } 310 | 311 | Defines { 312 | DIFFUSEMAP_ALPHA : DiffuseMap 313 | NUM_BONES : NumberOfBones 314 | } 315 | 316 | } 317 | 318 | Technique GBuf { 319 | 320 | VertexShader GLSL100: Common/MatDefs/Light/GBuf.vert 321 | FragmentShader GLSL100: Common/MatDefs/Light/GBuf.frag 322 | 323 | WorldParameters { 324 | WorldViewProjectionMatrix 325 | NormalMatrix 326 | WorldViewMatrix 327 | WorldMatrix 328 | } 329 | 330 | Defines { 331 | VERTEX_COLOR : UseVertexColor 332 | MATERIAL_COLORS : UseMaterialColors 333 | V_TANGENT : VTangent 334 | MINNAERT : Minnaert 335 | WARDISO : WardIso 336 | 337 | DIFFUSEMAP : DiffuseMap 338 | NORMALMAP : NormalMap 339 | SPECULARMAP : SpecularMap 340 | PARALLAXMAP : ParallaxMap 341 | } 342 | } 343 | 344 | Technique { 345 | LightMode FixedPipeline 346 | } 347 | 348 | Technique Glow { 349 | 350 | VertexShader GLSL100: Common/MatDefs/Misc/Unshaded.vert 351 | FragmentShader GLSL100: Common/MatDefs/Light/Glow.frag 352 | 353 | WorldParameters { 354 | WorldViewProjectionMatrix 355 | } 356 | 357 | Defines { 358 | NEED_TEXCOORD1 359 | HAS_GLOWMAP : GlowMap 360 | HAS_GLOWCOLOR : GlowColor 361 | 362 | NUM_BONES : NumberOfBones 363 | } 364 | } 365 | 366 | } 367 | -------------------------------------------------------------------------------- /assets/MatDefs/TrilinearLighting.vert: -------------------------------------------------------------------------------- 1 | #define ATTENUATION 2 | //#define HQ_ATTENUATION 3 | 4 | #import "Common/ShaderLib/Skinning.glsllib" 5 | #import "MatDefs/VertScattering.glsllib" 6 | 7 | uniform mat4 g_WorldViewProjectionMatrix; 8 | uniform mat4 g_WorldViewMatrix; 9 | uniform mat4 g_WorldMatrix; 10 | uniform mat3 g_NormalMatrix; 11 | uniform mat4 g_ViewMatrix; 12 | uniform vec3 g_CameraPosition; 13 | 14 | uniform vec4 m_Ambient; 15 | uniform vec4 m_Diffuse; 16 | uniform vec4 m_Specular; 17 | uniform float m_Shininess; 18 | 19 | uniform vec4 g_LightColor; 20 | uniform vec4 g_LightPosition; 21 | uniform vec4 g_AmbientLightColor; 22 | 23 | // Trilinear mapping related settings 24 | uniform vec3 m_WorldOffset; 25 | 26 | varying vec3 worldNormal; 27 | varying float z; 28 | 29 | varying vec3 worldTangent; 30 | 31 | // end trilinear settings 32 | 33 | varying vec3 texCoord; 34 | #ifdef SEPARATE_TEXCOORD 35 | varying vec2 texCoord2; 36 | attribute vec2 inTexCoord2; 37 | #endif 38 | 39 | varying vec3 AmbientSum; 40 | varying vec4 DiffuseSum; 41 | varying vec3 SpecularSum; 42 | 43 | attribute vec3 inPosition; 44 | attribute vec2 inTexCoord; 45 | attribute vec3 inNormal; 46 | 47 | varying vec3 lightVec; 48 | //varying vec4 spotVec; 49 | 50 | #ifdef VERTEX_COLOR 51 | attribute vec4 inColor; 52 | #endif 53 | 54 | #ifndef VERTEX_LIGHTING 55 | attribute vec4 inTangent; 56 | 57 | #ifndef NORMALMAP 58 | varying vec3 vNormal; 59 | #else 60 | // For debugging we want it either way 61 | varying vec3 vNormal; 62 | #endif 63 | //varying vec3 vPosition; 64 | varying vec3 vViewDir; 65 | varying vec4 vLightDir; 66 | #else 67 | varying vec2 vertexLightValues; 68 | uniform vec4 g_LightDirection; 69 | #endif 70 | 71 | 72 | #ifdef USE_REFLECTION 73 | uniform vec3 g_CameraPosition; 74 | uniform mat4 g_WorldMatrix; 75 | 76 | uniform vec3 m_FresnelParams; 77 | varying vec4 refVec; 78 | 79 | 80 | /** 81 | * Input: 82 | * attribute inPosition 83 | * attribute inNormal 84 | * uniform g_WorldMatrix 85 | * uniform g_CameraPosition 86 | * 87 | * Output: 88 | * varying refVec 89 | */ 90 | void computeRef(in vec4 modelSpacePos){ 91 | vec3 worldPos = (g_WorldMatrix * modelSpacePos).xyz; 92 | 93 | vec3 I = normalize( g_CameraPosition - worldPos ).xyz; 94 | vec3 N = normalize( (g_WorldMatrix * vec4(inNormal, 0.0)).xyz ); 95 | 96 | refVec.xyz = reflect(I, N); 97 | refVec.w = m_FresnelParams.x + m_FresnelParams.y * pow(1.0 + dot(I, N), m_FresnelParams.z); 98 | } 99 | #endif 100 | 101 | // JME3 lights in world space 102 | void lightComputeDir(in vec3 worldPos, in vec4 color, in vec4 position, out vec4 lightDir){ 103 | float posLight = step(0.5, color.w); 104 | vec3 tempVec = position.xyz * sign(posLight - 0.5) - (worldPos * posLight); 105 | lightVec = tempVec; 106 | #ifdef ATTENUATION 107 | float dist = length(tempVec); 108 | lightDir.w = clamp(1.0 - position.w * dist * posLight, 0.0, 1.0); 109 | lightDir.xyz = tempVec / vec3(dist); 110 | #else 111 | lightDir = vec4(normalize(tempVec), 1.0); 112 | #endif 113 | } 114 | 115 | #ifdef VERTEX_LIGHTING 116 | float lightComputeDiffuse(in vec3 norm, in vec3 lightdir){ 117 | return max(0.0, dot(norm, lightdir)); 118 | } 119 | 120 | float lightComputeSpecular(in vec3 norm, in vec3 viewdir, in vec3 lightdir, in float shiny){ 121 | if (shiny <= 1.0){ 122 | return 0.0; 123 | } 124 | #ifndef LOW_QUALITY 125 | vec3 H = (viewdir + lightdir) * vec3(0.5); 126 | return pow(max(dot(H, norm), 0.0), shiny); 127 | #else 128 | return 0.0; 129 | #endif 130 | } 131 | 132 | vec2 computeLighting(in vec3 wvPos, in vec3 wvNorm, in vec3 wvViewDir, in vec4 wvLightPos){ 133 | vec4 lightDir; 134 | lightComputeDir(wvPos, g_LightColor, wvLightPos, lightDir); 135 | float spotFallOff = 1.0; 136 | if(g_LightDirection.w != 0.0){ 137 | vec3 L=normalize(lightVec.xyz); 138 | vec3 spotdir = normalize(g_LightDirection.xyz); 139 | float curAngleCos = dot(-L, spotdir); 140 | float innerAngleCos = floor(g_LightDirection.w) * 0.001; 141 | float outerAngleCos = fract(g_LightDirection.w); 142 | float innerMinusOuter = innerAngleCos - outerAngleCos; 143 | spotFallOff = clamp((curAngleCos - outerAngleCos) / innerMinusOuter, 0.0, 1.0); 144 | } 145 | float diffuseFactor = lightComputeDiffuse(wvNorm, lightDir.xyz); 146 | float specularFactor = lightComputeSpecular(wvNorm, wvViewDir, lightDir.xyz, m_Shininess); 147 | //specularFactor *= step(0.01, diffuseFactor); 148 | return vec2(diffuseFactor, specularFactor) * vec2(lightDir.w)*spotFallOff; 149 | } 150 | #endif 151 | 152 | void main(){ 153 | vec4 modelSpacePos = vec4(inPosition, 1.0); 154 | vec3 modelSpaceNorm = inNormal; 155 | 156 | #ifndef VERTEX_LIGHTING 157 | vec3 modelSpaceTan = inTangent.xyz; 158 | #endif 159 | 160 | #ifdef NUM_BONES 161 | #ifndef VERTEX_LIGHTING 162 | Skinning_Compute(modelSpacePos, modelSpaceNorm, modelSpaceTan); 163 | #else 164 | Skinning_Compute(modelSpacePos, modelSpaceNorm); 165 | #endif 166 | #endif 167 | 168 | gl_Position = g_WorldViewProjectionMatrix * modelSpacePos; 169 | 170 | // Trilinear mapping stores the world location of the vertex in the 171 | // texture coordinate. Not so much for mapping as for noise lookups 172 | texCoord = (g_WorldMatrix * modelSpacePos).xyz + m_WorldOffset; 173 | 174 | // We keep track of the world normal. This calculation would be 175 | // incorrect with non-linear scaling but we know our source mesh 176 | // shouldn't have that. 177 | worldNormal = (g_WorldMatrix * vec4(modelSpaceNorm, 0.0)).xyz; 178 | 179 | //texCoord = inTexCoord; 180 | #ifdef SEPARATE_TEXCOORD 181 | texCoord2 = inTexCoord2; 182 | #endif 183 | 184 | #ifdef USE_SCATTERING 185 | vec4 wPos = g_WorldMatrix * modelSpacePos; 186 | calculateVertexGroundScattering(wPos.xyz, g_CameraPosition); 187 | #endif 188 | 189 | vec3 wvPosition = (g_WorldViewMatrix * modelSpacePos).xyz; 190 | z = length(wvPosition); 191 | vec3 wvNormal = normalize(g_NormalMatrix * modelSpaceNorm); 192 | vec3 viewDir = normalize(-wvPosition); 193 | 194 | //vec4 lightColor = g_LightColor[gl_InstanceID]; 195 | //vec4 lightPos = g_LightPosition[gl_InstanceID]; 196 | //vec4 wvLightPos = (g_ViewMatrix * vec4(lightPos.xyz, lightColor.w)); 197 | //wvLightPos.w = lightPos.w; 198 | 199 | vec4 wvLightPos = (g_ViewMatrix * vec4(g_LightPosition.xyz,clamp(g_LightColor.w,0.0,1.0))); 200 | wvLightPos.w = g_LightPosition.w; 201 | vec4 lightColor = g_LightColor; 202 | 203 | #if defined(NORMALMAP) && !defined(VERTEX_LIGHTING) 204 | // *** For trinlinear mapping we calculate the tangent 205 | // directly from the normal. 206 | //vec3 wvTangent = normalize(g_NormalMatrix * modelSpaceTan); 207 | //vec3 wvBinormal = cross(wvNormal, wvTangent); 208 | 209 | //mat3 tbnMat = mat3(wvTangent, wvBinormal * -inTangent.w,wvNormal); 210 | 211 | // We should probably be doing the tangent generation on the 212 | // frag side but JME is already setup to do lighting in view space 213 | // and I don't really want to unwind it. This is why we need the 214 | // tangent space here so that we can find the proper tangent-space 215 | // lighting vector. 216 | vec3 wn = normalize(worldNormal); 217 | 218 | #define BASIC_TANGENTS 219 | #ifdef BASIC_TANGENTS 220 | vec3 wTangentX = vec3(0.0, 0.0, -1.0); 221 | vec3 wTangentY = vec3(1.0, 0.0, 0.0); 222 | vec3 wTangentZ = vec3(1.0, 0.0, 0.0); 223 | 224 | // You'd think we might need to flip the signs but we 225 | // don't do it for the texture lookups so it's important 226 | // that each axis be reflective. 227 | vec3 blend = abs(wn); 228 | blend /= blend.x + blend.y + blend.z; 229 | #else 230 | vec3 wTangentX = normalize(vec3(wn.z, 0.0, -wn.x)); 231 | vec3 wTangentY = normalize(vec3(wn.y, -wn.x, 0.0)); 232 | vec3 wTangentZ = normalize(vec3(wn.z, 0.0, -wn.x)); 233 | 234 | // Tangent vectors already include signs so mix 235 | // based on abs 236 | vec3 blend = abs(wn); 237 | blend /= blend.x + blend.y + blend.z; 238 | #endif 239 | 240 | // Both of the above approaches leave some oddly bright 241 | // areas of the terrain. I think this is due to dramatic 242 | // interpolation across faces with extremely divergent 243 | // normals. If it's true then the only way to solve it 244 | // would be to do lighting in world space and just assume 245 | // tangents on the frag side. 246 | 247 | // which I think if I just pretend that normal maps don't 248 | // exist here then I can do all tangent stuff on the frag side. 249 | 250 | worldTangent = normalize(wTangentX * blend.x 251 | + wTangentY * blend.y 252 | + wTangentZ * blend.z); 253 | vec3 wvTangent = normalize((g_ViewMatrix * vec4(worldTangent, 0.0)).xyz); 254 | vec3 wvBinormal = normalize(cross(wvNormal, wvTangent)); 255 | wvTangent = normalize(cross(wvBinormal, wvNormal)); 256 | mat3 tbnMat = mat3(wvTangent, wvBinormal * -1.0, wvNormal); 257 | 258 | //vPosition = wvPosition * tbnMat; 259 | //vViewDir = viewDir * tbnMat; 260 | vViewDir = -wvPosition * tbnMat; 261 | lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir); 262 | vLightDir.xyz = (vLightDir.xyz * tbnMat).xyz; 263 | 264 | // For debugging purposes. Note: when normal maps are used 265 | // the light is in tangent space. In tangent space the normal 266 | // is just a z unit vector 267 | vNormal = vec3(0.0, 0.0, 1.0); //wvNormal; 268 | #elif !defined(VERTEX_LIGHTING) 269 | vNormal = wvNormal; 270 | 271 | //vPosition = wvPosition; 272 | vViewDir = viewDir; 273 | 274 | lightComputeDir(wvPosition, lightColor, wvLightPos, vLightDir); 275 | 276 | #ifdef V_TANGENT 277 | vNormal = normalize(g_NormalMatrix * inTangent.xyz); 278 | vNormal = -cross(cross(vLightDir.xyz, vNormal), vNormal); 279 | #endif 280 | #endif 281 | 282 | //computing spot direction in view space and unpacking spotlight cos 283 | // spotVec = (g_ViewMatrix * vec4(g_LightDirection.xyz, 0.0) ); 284 | // spotVec.w = floor(g_LightDirection.w) * 0.001; 285 | // lightVec.w = fract(g_LightDirection.w); 286 | 287 | lightColor.w = 1.0; 288 | #ifdef MATERIAL_COLORS 289 | AmbientSum = (m_Ambient * g_AmbientLightColor).rgb; 290 | DiffuseSum = m_Diffuse * lightColor; 291 | SpecularSum = (m_Specular * lightColor).rgb; 292 | #else 293 | AmbientSum = vec3(0.2, 0.2, 0.2) * g_AmbientLightColor.rgb; // Default: ambient color is dark gray 294 | DiffuseSum = lightColor; 295 | SpecularSum = vec3(0.0); 296 | #endif 297 | 298 | #ifdef VERTEX_COLOR 299 | AmbientSum *= inColor.rgb; 300 | DiffuseSum *= inColor; 301 | #endif 302 | 303 | #ifdef VERTEX_LIGHTING 304 | vertexLightValues = computeLighting(wvPosition, wvNormal, viewDir, wvLightPos); 305 | #endif 306 | 307 | #ifdef USE_REFLECTION 308 | computeRef(modelSpacePos); 309 | #endif 310 | } 311 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Builds, tests, and runs the project BasicGameTemplate. 12 | 13 | 14 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /nbproject/assets-impl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=94bf7c61 2 | build.xml.script.CRC32=79a29eb7 3 | build.xml.stylesheet.CRC32=958a1d3e@1.32.1.45 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=6c4d25c0 7 | nbproject/build-impl.xml.script.CRC32=17b92ddc 8 | nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48 9 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | application.title=IsoSurfaceLib 6 | application.vendor=Simsilica, LLC 7 | assets.jar.name=iso-surface-assets.jar 8 | assets.excludes=**/*.j3odata,**/*.mesh,**/*.skeleton,**/*.mesh.xml,**/*.skeleton.xml,**/*.scene,**/*.material,**/*.obj,**/*.mtl,**/*.3ds,**/*.dae,**/*.blend,**/*.blend*[0-9],**/.backups/**,**/*.psd 9 | assets.folder.name=assets 10 | assets.compress=true 11 | build.classes.dir=${build.dir}/classes 12 | build.classes.excludes=**/*.java,**/*.form,**/.backups/** 13 | # This directory is removed when the project is cleaned: 14 | build.dir=build 15 | build.generated.dir=${build.dir}/generated 16 | build.generated.sources.dir=${build.dir}/generated-sources 17 | # Only compile against the classpath explicitly listed here: 18 | build.sysclasspath=ignore 19 | build.test.classes.dir=${build.dir}/test/classes 20 | build.test.results.dir=${build.dir}/test/results 21 | compile.on.save=true 22 | # Uncomment to specify the preferred debugger connection transport: 23 | #debug.transport=dt_socket 24 | debug.classpath=\ 25 | ${run.classpath} 26 | debug.test.classpath=\ 27 | ${run.test.classpath} 28 | # This directory is removed when the project is cleaned: 29 | dist.dir=dist 30 | dist.jar=${dist.dir}/${application.title}.jar 31 | dist.javadoc.dir=${dist.dir}/javadoc 32 | endorsed.classpath= 33 | excludes= 34 | file.reference.guava-12.0.jar-1=lib\\guava-12.0.jar 35 | file.reference.slf4j-api-1.7.5.jar-1=lib\\slf4j-api-1.7.5.jar 36 | includes=** 37 | jar.compress=true 38 | javac.classpath=\ 39 | ${reference.Pager.jar}:\ 40 | ${file.reference.guava-12.0.jar-1}:\ 41 | ${file.reference.slf4j-api-1.7.5.jar-1}:\ 42 | ${libs.jme3-core.classpath} 43 | # Space-separated list of extra javac options 44 | javac.compilerargs= 45 | javac.deprecation=false 46 | javac.processorpath=\ 47 | ${javac.classpath} 48 | javac.source=1.6 49 | javac.target=1.6 50 | javac.test.classpath=\ 51 | ${javac.classpath}:\ 52 | ${build.classes.dir} 53 | javadoc.additionalparam= 54 | javadoc.author=false 55 | javadoc.encoding=${source.encoding} 56 | javadoc.noindex=false 57 | javadoc.nonavbar=false 58 | javadoc.notree=false 59 | javadoc.private=false 60 | javadoc.splitindex=true 61 | javadoc.use=true 62 | javadoc.version=false 63 | javadoc.windowtitle= 64 | jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api" 65 | jnlp.codebase.type=local 66 | jnlp.descriptor=application 67 | jnlp.enabled=false 68 | jnlp.offline-allowed=false 69 | jnlp.signed=false 70 | main.class=mygame.Main 71 | meta.inf.dir=${src.dir}/META-INF 72 | manifest.file=MANIFEST.MF 73 | mkdist.disabled=false 74 | platform.active=JDK_1.7 75 | project.Pager=../Pager 76 | reference.Pager.jar=${project.Pager}/dist/Pager.jar 77 | run.classpath=\ 78 | ${javac.classpath}:\ 79 | ${build.classes.dir}:\ 80 | ${assets.folder.name} 81 | # Space-separated list of JVM arguments used when running the project 82 | # (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value 83 | # or test-sys-prop.name=value to set system properties for unit tests): 84 | run.jvmargs= 85 | run.test.classpath=\ 86 | ${javac.test.classpath}:\ 87 | ${build.test.classes.dir} 88 | source.encoding=UTF-8 89 | src.java.dir=src\\main\\java 90 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | IsoSurface 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | Pager 21 | jar 22 | 23 | jar 24 | clean 25 | jar 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/DensityVolume.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso; 38 | 39 | import com.jme3.math.Vector3f; 40 | 41 | 42 | /** 43 | * Represents a volume of space from which density 44 | * values may be sampled. Default implementations can be 45 | * found in com.simsilica.iso.volume. 46 | * 47 | * @author Paul Speed 48 | */ 49 | public interface DensityVolume { 50 | 51 | /** 52 | * Retrieves a density value at a specific integer grid corner. 53 | * For some density volumes this may be optimized to avoid 54 | * trilinear interpolation. 55 | */ 56 | public float getDensity( int x, int y, int z ); 57 | 58 | /** 59 | * Retrieves a density value from the specified point in 60 | * this volume. 61 | */ 62 | public float getDensity( float x, float y, float z ); 63 | 64 | /** 65 | * Retrieves the field direction at a particular point in this 66 | * volume. Field direction can be used to define surface normals, 67 | * potentially predict collisions, etc.. 68 | */ 69 | public Vector3f getFieldDirection( float x, float y, float z, Vector3f target ); 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/IsoTerrainZone.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso; 38 | 39 | import com.google.common.base.Supplier; 40 | import com.jme3.material.Material; 41 | import com.jme3.math.Vector3f; 42 | import com.jme3.renderer.queue.RenderQueue; 43 | import com.jme3.scene.Geometry; 44 | import com.jme3.scene.Mesh; 45 | import com.jme3.scene.Node; 46 | import com.jme3.scene.Spatial; 47 | import com.jme3.scene.VertexBuffer; 48 | import com.jme3.util.BufferUtils; 49 | import com.simsilica.builder.Builder; 50 | import com.simsilica.iso.volume.ArrayDensityVolume; 51 | import com.simsilica.pager.Grid; 52 | import com.simsilica.pager.Zone; 53 | import java.util.concurrent.locks.ReentrantLock; 54 | import org.slf4j.Logger; 55 | import org.slf4j.LoggerFactory; 56 | 57 | 58 | /** 59 | * A Zone implementation that uses a MeshGenerator and a 60 | * density field to generator a terrain section. 61 | * 62 | * @author Paul Speed 63 | */ 64 | public class IsoTerrainZone implements Zone { 65 | 66 | static Logger log = LoggerFactory.getLogger(IsoTerrainZone.class); 67 | 68 | private int xCell; 69 | private int yCell; 70 | private int zCell; 71 | private Grid grid; 72 | private Vector3f volumeSize; 73 | private Vector3f volumeOffset; 74 | private DensityVolume source; 75 | private int priority; 76 | private Node node; 77 | private Spatial land; 78 | private Spatial wire; 79 | private Supplier generator; 80 | private Material terrainMaterial; 81 | private boolean generateCollisionData; 82 | 83 | // For testing to make sure the builder and the pager are not 84 | // competing. Leaving it in is just a bit of paranoia 85 | private final ReentrantLock accessLock = new ReentrantLock(); 86 | 87 | private static ThreadLocal cachedVolume = new ThreadLocal(); 88 | 89 | public IsoTerrainZone( int xCell, int yCell, int zCell, Grid grid, 90 | Vector3f volumeSize, Vector3f volumeOffset, 91 | DensityVolume source, 92 | Supplier generator, 93 | Material terrainMaterial, 94 | boolean generateCollisionData ) 95 | { 96 | this.xCell = xCell; 97 | this.yCell = yCell; 98 | this.zCell = zCell; 99 | this.grid = grid; 100 | this.volumeSize = volumeSize; 101 | this.volumeOffset = volumeOffset; 102 | this.source = source; 103 | this.generator = generator; 104 | this.terrainMaterial = terrainMaterial; 105 | this.generateCollisionData = generateCollisionData; 106 | this.node = new Node("Terrain[" + xCell + ", " + yCell + ", " + zCell + "]"); 107 | } 108 | 109 | public boolean setRelativeGridLocation( int x, int y, int z ) { 110 | return false; 111 | } 112 | 113 | public boolean setViewLocation( float x, float z ) { 114 | return false; 115 | } 116 | 117 | public Grid getGrid() { 118 | return grid; 119 | } 120 | 121 | public int getXCell() { 122 | return xCell; 123 | } 124 | 125 | public int getYCell() { 126 | return yCell; 127 | } 128 | 129 | public int getZCell() { 130 | return zCell; 131 | } 132 | 133 | public float getXWorld() { 134 | return grid.toWorldX(xCell); 135 | } 136 | 137 | public float getYWorld() { 138 | return grid.toWorldY(yCell); 139 | } 140 | 141 | public float getZWorld() { 142 | return grid.toWorldZ(zCell); 143 | } 144 | 145 | public Vector3f getWorldLocation( Vector3f target ) { 146 | return grid.toWorld(xCell, yCell, zCell, target); 147 | } 148 | 149 | public void setMeshGenerator( Supplier generator ) { 150 | this.generator = generator; 151 | } 152 | 153 | public void resetPriority( int xCenter, int yCenter, int zCenter, int bias ) 154 | { 155 | int dx = xCell - xCenter; 156 | int dz = zCell - zCenter; 157 | this.priority = bias * (int)Math.sqrt(dx * dx + dz * dz); 158 | } 159 | 160 | public int getPriority() { 161 | return priority; 162 | } 163 | 164 | public Node getNode() { 165 | return node; 166 | } 167 | 168 | public Node getZoneRoot() { 169 | return node; 170 | } 171 | 172 | public void setParentZone( Zone parentZone ) { 173 | } 174 | 175 | public void build() { 176 | if( log.isInfoEnabled() ) { 177 | log.info("Building:" + xCell + ", " + yCell + ", " + zCell + " priority:" + priority); 178 | } 179 | 180 | accessLock.lock(); 181 | try { 182 | long start = System.nanoTime(); 183 | 184 | int x = (int)(volumeOffset.x + xCell * volumeSize.x); 185 | int y = (int)(volumeOffset.y + yCell * volumeSize.y); 186 | int z = (int)(volumeOffset.z + zCell * volumeSize.z); 187 | 188 | MeshGenerator mg = generator.get(); 189 | Vector3f size = mg.getRequiredVolumeSize(); 190 | int cx = (int)size.x; 191 | int cy = (int)size.y; 192 | int cz = (int)size.z; 193 | 194 | // Note: we presume in this caching strategy that the 195 | // required volume size never changes. But that's a reasonable 196 | // assumption since if it did then a new terrain pager or factory 197 | // should have been created for about a dozen other reasons. 198 | ArrayDensityVolume volume = cachedVolume.get(); 199 | if( volume == null ) { 200 | volume = ArrayDensityVolume.extractVolume(source, x, y, z, cx, cy, cz); 201 | cachedVolume.set(volume); 202 | } else { 203 | volume.extract(source, x, y, z); 204 | } 205 | 206 | long time1 = System.nanoTime(); 207 | long time2 = time1; 208 | 209 | Mesh landMesh = generator.get().buildMesh(volume); 210 | if( landMesh != null ) { 211 | land = createLand(landMesh, false); 212 | if( log.isDebugEnabled() ) { 213 | log.debug("volume size:" + volumeSize); 214 | log.debug("bounding shape:" + landMesh.getBound()); 215 | } 216 | land.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); 217 | time2 = System.nanoTime(); 218 | if( generateCollisionData ) { 219 | landMesh.createCollisionData(); 220 | } 221 | } else { 222 | land = null; 223 | log.debug("Empty mesh."); 224 | } 225 | long end = System.nanoTime(); 226 | 227 | if( log.isInfoEnabled() ) { 228 | log.info("Total generation time:" + ((end-start)/1000000.0) + " ms"); 229 | log.info(" Density field:" + ((time1-start)/1000000.0) + " ms"); 230 | log.info(" Mesh generation:" + ((time2-time1)/1000000.0) + " ms"); 231 | log.info(" Collision data generation:" + ((end-time2)/1000000.0) + " ms"); 232 | } 233 | } finally { 234 | accessLock.unlock(); 235 | } 236 | } 237 | 238 | @Override 239 | public void apply( Builder builder ) { 240 | if( !accessLock.tryLock() ) { 241 | throw new IllegalStateException("Thread is still building."); 242 | } 243 | try { 244 | if( land != null ) { 245 | node.attachChild(land); 246 | } 247 | } finally { 248 | accessLock.unlock(); 249 | } 250 | } 251 | 252 | @Override 253 | public void release( Builder builder ) { 254 | if( log.isTraceEnabled() ) { 255 | log.trace("IsoLandReference.release():" + this ); 256 | } 257 | 258 | if( !accessLock.tryLock() ) { 259 | throw new IllegalStateException("Thread is still building."); 260 | } 261 | try { 262 | // Should queue the buffers to be freed 263 | // or something. 264 | // ...for now we will free them directly. 265 | if( land != null ) { 266 | Mesh mesh = ((Geometry)land).getMesh(); 267 | for( VertexBuffer vb : mesh.getBufferList() ) { 268 | if( log.isTraceEnabled() ) { 269 | log.trace("--destroying buffer:" + vb ); 270 | } 271 | BufferUtils.destroyDirectBuffer( vb.getData() ); 272 | } 273 | } 274 | } finally { 275 | accessLock.unlock(); 276 | } 277 | } 278 | 279 | protected Spatial createLand( Mesh mesh, boolean glass ) 280 | { 281 | Geometry geom = new Geometry("landGeometry[" + xCell + ", " + yCell + ", " + zCell + "]", mesh); 282 | geom.setMaterial(terrainMaterial); 283 | return geom; 284 | } 285 | 286 | /*protected Spatial createWireLand( Mesh mesh ) 287 | { 288 | Geometry geom = new Geometry("wireLandGeometry[" + xCell + ", " + yCell + ", " + zCell + "]", mesh); 289 | geom.setMaterial(wireMaterial); 290 | geom.setQueueBucket(Bucket.Transparent); 291 | return geom; 292 | }*/ 293 | 294 | @Override 295 | public String toString() { 296 | return "IsoLandReference[" + xCell + ", " + yCell + ", " + zCell + "]"; 297 | } 298 | } 299 | 300 | 301 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/IsoTerrainZoneFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso; 38 | 39 | import com.google.common.base.Supplier; 40 | import com.jme3.material.Material; 41 | import com.jme3.math.Vector3f; 42 | import com.simsilica.pager.PagedGrid; 43 | import com.simsilica.pager.Zone; 44 | import com.simsilica.pager.ZoneFactory; 45 | 46 | 47 | /** 48 | * Creates IsoTerrainZones that carve out specific sections of 49 | * a supplied world DensityVolume and use a supplied MeshGenerator 50 | * to build meshes. 51 | * 52 | * @author Paul Speed 53 | */ 54 | public class IsoTerrainZoneFactory implements ZoneFactory { 55 | 56 | private DensityVolume worldVolume; 57 | private Supplier generator; 58 | private Vector3f volumeSize; 59 | private Vector3f volumeOffset; 60 | private Material terrainMaterial; 61 | private boolean generateCollisionData; 62 | 63 | public IsoTerrainZoneFactory( DensityVolume worldVolume, 64 | Vector3f volumeSize, Vector3f volumeOffset, 65 | Supplier generator, 66 | Material terrainMaterial, 67 | boolean generateCollisionData ) { 68 | this.worldVolume = worldVolume; 69 | this.generator = generator; 70 | this.volumeSize = volumeSize; 71 | this.volumeOffset = volumeOffset; 72 | this.terrainMaterial = terrainMaterial; 73 | this.generateCollisionData = generateCollisionData; 74 | } 75 | 76 | public Zone createZone( PagedGrid pg, int xCell, int yCell, int zCell ) { 77 | 78 | IsoTerrainZone result = new IsoTerrainZone(xCell, yCell, zCell, pg.getGrid(), 79 | volumeSize, volumeOffset, worldVolume, 80 | generator, terrainMaterial, 81 | generateCollisionData); 82 | 83 | return result; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/MeshGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso; 38 | 39 | import com.jme3.math.Vector3f; 40 | import com.jme3.scene.Mesh; 41 | 42 | 43 | /** 44 | * 45 | * @author Paul Speed 46 | */ 47 | public interface MeshGenerator { 48 | 49 | /** 50 | * Returns the size of the density volume required for mesh 51 | * generation. 52 | */ 53 | public Vector3f getRequiredVolumeSize(); 54 | 55 | /** 56 | * Returns the predicted size of the generated meshes. 57 | */ 58 | public Vector3f getGenerationSize(); 59 | 60 | /** 61 | * Builds a mesh from the specified density volume. 62 | */ 63 | public Mesh buildMesh( DensityVolume volume ); 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/collision/Collider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.collision; 38 | 39 | import com.jme3.math.Vector3f; 40 | 41 | 42 | /** 43 | * 44 | * 45 | * @author Paul Speed 46 | */ 47 | public interface Collider { 48 | 49 | public Contact getContact( Vector3f loc, float radius ); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/collision/Contact.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.collision; 38 | 39 | import com.jme3.math.Vector3f; 40 | 41 | 42 | /** 43 | * Simple collider to collider collision information. 44 | * 45 | * @author Paul Speed 46 | */ 47 | public class Contact { 48 | public Collider collider1; 49 | public Collider collider2; 50 | public Vector3f contactPoint; 51 | public Vector3f contactNormal; 52 | public float penetration; 53 | 54 | public Contact() { 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "Contact[cp=" + contactPoint + ", cn=" + contactNormal + ", pen=" + penetration + "]"; 60 | } 61 | } 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/collision/SimpleVolumeCollider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.collision; 38 | 39 | import com.jme3.math.Vector3f; 40 | import com.simsilica.iso.DensityVolume; 41 | 42 | 43 | /** 44 | * 45 | * 46 | * @author Paul Speed 47 | */ 48 | public class SimpleVolumeCollider implements Collider { 49 | private DensityVolume volume; 50 | 51 | public SimpleVolumeCollider( DensityVolume volume ) { 52 | this.volume = volume; 53 | } 54 | 55 | @Override 56 | public Contact getContact( Vector3f loc, float radius ) { 57 | 58 | // Check the density in the direction of the surface 59 | // to the best of our ability to estimate 60 | Vector3f dir = volume.getFieldDirection(1 + loc.x, 1 + loc.y, 1 + loc.z, null); 61 | float density = volume.getDensity(1 + loc.x - dir.x * radius, 62 | 1 + loc.y - dir.y * radius, 63 | 1 + loc.z - dir.z * radius); 64 | 65 | if( density < 0 ) { 66 | // We are not in contact 67 | return null; 68 | } 69 | 70 | // Else try to calculate the intersection 71 | float center = volume.getDensity(1 + loc.x, 1 + loc.y, 1 + loc.z); 72 | 73 | float pen; 74 | Vector3f cp; 75 | 76 | if( center > 0 ) { 77 | // we are deep in it... try to see if we can guess a way 78 | // out 79 | float outside = volume.getDensity(1 + loc.x + dir.x, 80 | 1 + loc.y + dir.y, 81 | 1 + loc.z + dir.z); 82 | float part = Math.abs(center) / Math.abs(center - outside); 83 | pen = part; 84 | //System.out.println( "outside:" + outside + " center:" + center + " part:" + part + " pen:" + pen ); 85 | 86 | // Calculate a contact point 87 | cp = loc.add(dir.mult(radius - pen)); 88 | //System.out.println( "loc:" + loc + " cp:" + cp ); 89 | } else { 90 | float part = Math.abs(density) / Math.abs(density - center); 91 | pen = radius * part; 92 | //System.out.println( "density:" + density + " center:" + center + " part:" + part + " pen:" + pen ); 93 | 94 | // Calculate a contact point 95 | cp = loc.add(dir.mult(radius - pen)); 96 | //System.out.println( "loc:" + loc + " cp:" + cp ); 97 | } 98 | 99 | Contact result = new Contact(); 100 | result.contactPoint = cp; 101 | result.contactNormal = volume.getFieldDirection(1 + cp.x, 1 + cp.y, 1 + cp.z, null); 102 | result.penetration = pen; 103 | 104 | return result; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/fractal/GemsFractalDensityVolume.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.fractal; 38 | 39 | import com.jme3.math.Quaternion; 40 | import com.jme3.math.Vector3f; 41 | import com.simsilica.iso.DensityVolume; 42 | 43 | 44 | /** 45 | * DensityVolume implementation returning densities based on the 46 | * fractal defined in the GPU Gems book. Some adaptations as necessary 47 | * to convert from a shader-based algorithm to a Java-based one. 48 | * 49 | * @author Paul Speed 50 | */ 51 | public class GemsFractalDensityVolume implements DensityVolume { 52 | 53 | private PerlinNoise noise = new PerlinNoise(0); 54 | private PerlinNoise noise2 = new PerlinNoise(1); 55 | private PerlinNoise noise3 = new PerlinNoise(2); 56 | 57 | private Quaternion octaveMat0; 58 | private Quaternion octaveMat1; 59 | private Quaternion octaveMat2; 60 | private Quaternion octaveMat3; 61 | private Quaternion octaveMat4; 62 | private Quaternion octaveMat5; 63 | private Quaternion octaveMat6; 64 | private Quaternion octaveMat7; 65 | 66 | 67 | public GemsFractalDensityVolume() { 68 | octaveMat0 = new Quaternion(); 69 | octaveMat1 = new Quaternion().fromAngles(0.1f, 0.01f, -0.1f); 70 | octaveMat2 = new Quaternion().fromAngles(0.2f, 0.02f, -0.2f); 71 | octaveMat3 = new Quaternion().fromAngles(0.3f, 0.03f, -0.3f); 72 | octaveMat4 = new Quaternion().fromAngles(0.4f, 0.04f, -0.4f); 73 | octaveMat5 = new Quaternion().fromAngles(0.5f, 0.05f, -0.5f); 74 | octaveMat6 = new Quaternion().fromAngles(0.6f, 0.06f, -0.6f); 75 | octaveMat7 = new Quaternion().fromAngles(0.7f, 0.07f, -0.7f); 76 | } 77 | 78 | private void warp( Vector3f loc, double frequency, double scale ) 79 | { 80 | double x = noise.getNoise(loc.x * frequency, loc.y * frequency, loc.z * frequency); 81 | double y = noise2.getNoise(loc.x * frequency, loc.y * frequency, loc.z * frequency); 82 | double z = noise3.getNoise(loc.x * frequency, loc.y * frequency, loc.z * frequency); 83 | 84 | loc.x = (float)(loc.x + x * scale); 85 | loc.y = (float)(loc.y + y * scale); 86 | loc.z = (float)(loc.z + z * scale); 87 | } 88 | 89 | private double getNoise( Vector3f loc, double scale ) 90 | { 91 | //scale *= 0.5f; 92 | //scale *= 4; // 16; 93 | //scale *= 10; //16; 94 | scale *= 16; 95 | return noise.getNoise(loc.x * scale, loc.y * scale, loc.z * scale); // * 2 - 1; 96 | } 97 | 98 | protected float density( Vector3f loc ) { 99 | 100 | double density = -loc.y; 101 | 102 | // Warp the location 103 | warp(loc, 0.004, 8); 104 | //warp(loc, 0.001, 40); 105 | 106 | Vector3f c0 = octaveMat0.mult(loc); 107 | Vector3f c1 = octaveMat1.mult(loc); 108 | Vector3f c2 = octaveMat2.mult(loc); 109 | Vector3f c3 = octaveMat3.mult(loc); 110 | Vector3f c4 = octaveMat4.mult(loc); 111 | Vector3f c5 = octaveMat5.mult(loc); 112 | Vector3f c6 = octaveMat6.mult(loc); 113 | Vector3f c7 = octaveMat7.mult(loc); 114 | 115 | density += getNoise(c0, 0.1600*1.021) * 0.32*1.16; 116 | density += getNoise(c1, 0.0800*0.985) * 0.64*1.12; 117 | density += getNoise(c2, 0.0400*1.051) * 1.28*1.08; 118 | density += getNoise(c3, 0.0200*1.020) * 2.56*1.04; 119 | density += getNoise(c4, 0.0100*0.968) * 5; 120 | density += getNoise(c5, 0.0050*0.994) * 10; 121 | density += getNoise(c6, 0.0025*1.045) * 20*0.9; 122 | density += getNoise(c7, 0.0012*0.972) * 40*0.8; 123 | 124 | return (float)density; 125 | } 126 | 127 | public float getDensity( int x, int y, int z ) { 128 | return density(new Vector3f(x, y, z)); 129 | } 130 | 131 | public float getDensity( float x, float y, float z ) { 132 | return density(new Vector3f(x, y, z)); 133 | } 134 | 135 | public Vector3f getFieldDirection(float x, float y, float z, Vector3f target) { 136 | 137 | float d = 1f; 138 | 139 | double nx = getDensity(x + d, y, z) 140 | - getDensity(x - d, y, z); 141 | double ny = getDensity(x, y + d, z) 142 | - getDensity(x, y - d, z); 143 | double nz = getDensity(x, y, z + d) 144 | - getDensity(x, y, z - d); 145 | 146 | if( target == null ) { 147 | target = new Vector3f((float)-nx, (float)-ny, (float)-nz).normalizeLocal(); 148 | } else { 149 | target.set((float)-nx, (float)-ny, (float)-nz); 150 | target.normalizeLocal(); 151 | } 152 | 153 | return target; 154 | } 155 | } 156 | 157 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/mc/MarchingCubesConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.mc; 38 | 39 | 40 | /** 41 | * Constants used internal to the Marching Cubes mesh generator. 42 | * 43 | * @author Paul Speed 44 | */ 45 | public class MarchingCubesConstants { 46 | 47 | public static int[][] edgeStarts = { 48 | {0, 0, 0}, {0, 1, 0}, {1, 0, 0}, {0, 0, 0}, 49 | {0, 0, 1}, {0, 1, 1}, {1, 0, 1}, {0, 0, 1}, 50 | {0, 0, 0}, {0, 1, 0}, {1, 1, 0}, {1, 0, 0} 51 | }; 52 | 53 | public static int[][] edgeDirs = { 54 | {0, 1, 0}, {1, 0, 0}, {0, 1, 0}, {1, 0, 0}, 55 | {0, 1, 0}, {1, 0, 0}, {0, 1, 0}, {1, 0, 0}, 56 | {0, 0, 1}, {0, 0, 1}, {0, 0, 1}, {0, 0, 1} 57 | }; 58 | 59 | public static int[][] edgeEnds = { 60 | {0, 1, 0}, {1, 1, 0 }, {1, 1, 0}, {1, 0, 0}, 61 | {0, 1, 1}, {1, 1, 1 }, {1, 1, 1}, {1, 0, 1}, 62 | {0, 0, 1}, {0, 1, 1 }, {1, 1, 1}, {1, 0, 1} 63 | }; 64 | 65 | // Raw tables 66 | public static int[][][] triEdges = new int[][][] { 67 | {}, 68 | {{ 0, 8, 3 }}, 69 | {{ 0, 1, 9 }}, 70 | {{ 1, 8, 3 }, { 9, 8, 1 }}, 71 | {{ 1, 2, 10 }}, 72 | {{ 0, 8, 3 }, { 1, 2, 10 }}, 73 | {{ 9, 2, 10 }, { 0, 2, 9 }}, 74 | {{ 2, 8, 3 }, { 2, 10, 8 }, { 10, 9, 8 }}, 75 | {{ 3, 11, 2 }}, 76 | {{ 0, 11, 2 }, { 8, 11, 0 }}, 77 | {{ 1, 9, 0 }, { 2, 3, 11 }}, 78 | {{ 1, 11, 2 }, { 1, 9, 11 }, { 9, 8, 11 }}, 79 | {{ 3, 10, 1 }, { 11, 10, 3 }}, 80 | {{ 0, 10, 1 }, { 0, 8, 10 }, { 8, 11, 10 }}, 81 | {{ 3, 9, 0 }, { 3, 11, 9 }, { 11, 10, 9 }}, 82 | {{ 9, 8, 10 }, { 10, 8, 11 }}, 83 | {{ 4, 7, 8 }}, 84 | {{ 4, 3, 0 }, { 7, 3, 4 }}, 85 | {{ 0, 1, 9 }, { 8, 4, 7 }}, 86 | {{ 4, 1, 9 }, { 4, 7, 1 }, { 7, 3, 1 }}, 87 | {{ 1, 2, 10 }, { 8, 4, 7 }}, 88 | {{ 3, 4, 7 }, { 3, 0, 4 }, { 1, 2, 10 }}, 89 | {{ 9, 2, 10 }, { 9, 0, 2 }, { 8, 4, 7 }}, 90 | {{ 2, 10, 9 }, { 2, 9, 7 }, { 2, 7, 3 }, { 7, 9, 4 }}, 91 | {{ 8, 4, 7 }, { 3, 11, 2 }}, 92 | {{ 11, 4, 7 }, { 11, 2, 4 }, { 2, 0, 4 }}, 93 | {{ 9, 0, 1 }, { 8, 4, 7 }, { 2, 3, 11 }}, 94 | {{ 4, 7, 11 }, { 9, 4, 11 }, { 9, 11, 2 }, { 9, 2, 1 }}, 95 | {{ 3, 10, 1 }, { 3, 11, 10 }, { 7, 8, 4 }}, 96 | {{ 1, 11, 10 }, { 1, 4, 11 }, { 1, 0, 4 }, { 7, 11, 4 }}, 97 | {{ 4, 7, 8 }, { 9, 0, 11 }, { 9, 11, 10 }, { 11, 0, 3 }}, 98 | {{ 4, 7, 11 }, { 4, 11, 9 }, { 9, 11, 10 }}, 99 | {{ 9, 5, 4 }}, 100 | {{ 9, 5, 4 }, { 0, 8, 3 }}, 101 | {{ 0, 5, 4 }, { 1, 5, 0 }}, 102 | {{ 8, 5, 4 }, { 8, 3, 5 }, { 3, 1, 5 }}, 103 | {{ 1, 2, 10 }, { 9, 5, 4 }}, 104 | {{ 3, 0, 8 }, { 1, 2, 10 }, { 4, 9, 5 }}, 105 | {{ 5, 2, 10 }, { 5, 4, 2 }, { 4, 0, 2 }}, 106 | {{ 2, 10, 5 }, { 3, 2, 5 }, { 3, 5, 4 }, { 3, 4, 8 }}, 107 | {{ 9, 5, 4 }, { 2, 3, 11 }}, 108 | {{ 0, 11, 2 }, { 0, 8, 11 }, { 4, 9, 5 }}, 109 | {{ 0, 5, 4 }, { 0, 1, 5 }, { 2, 3, 11 }}, 110 | {{ 2, 1, 5 }, { 2, 5, 8 }, { 2, 8, 11 }, { 4, 8, 5 }}, 111 | {{ 10, 3, 11 }, { 10, 1, 3 }, { 9, 5, 4 }}, 112 | {{ 4, 9, 5 }, { 0, 8, 1 }, { 8, 10, 1 }, { 8, 11, 10 }}, 113 | {{ 5, 4, 0 }, { 5, 0, 11 }, { 5, 11, 10 }, { 11, 0, 3 }}, 114 | {{ 5, 4, 8 }, { 5, 8, 10 }, { 10, 8, 11 }}, 115 | {{ 9, 7, 8 }, { 5, 7, 9 }}, 116 | {{ 9, 3, 0 }, { 9, 5, 3 }, { 5, 7, 3 }}, 117 | {{ 0, 7, 8 }, { 0, 1, 7 }, { 1, 5, 7 }}, 118 | {{ 1, 5, 3 }, { 3, 5, 7 }}, 119 | {{ 9, 7, 8 }, { 9, 5, 7 }, { 10, 1, 2 }}, 120 | {{ 10, 1, 2 }, { 9, 5, 0 }, { 5, 3, 0 }, { 5, 7, 3 }}, 121 | {{ 8, 0, 2 }, { 8, 2, 5 }, { 8, 5, 7 }, { 10, 5, 2 }}, 122 | {{ 2, 10, 5 }, { 2, 5, 3 }, { 3, 5, 7 }}, 123 | {{ 7, 9, 5 }, { 7, 8, 9 }, { 3, 11, 2 }}, 124 | {{ 9, 5, 7 }, { 9, 7, 2 }, { 9, 2, 0 }, { 2, 7, 11 }}, 125 | {{ 2, 3, 11 }, { 0, 1, 8 }, { 1, 7, 8 }, { 1, 5, 7 }}, 126 | {{ 11, 2, 1 }, { 11, 1, 7 }, { 7, 1, 5 }}, 127 | {{ 9, 5, 8 }, { 8, 5, 7 }, { 10, 1, 3 }, { 10, 3, 11 }}, 128 | {{ 5, 7, 0 }, { 5, 0, 9 }, { 7, 11, 0 }, { 1, 0, 10 }, { 11, 10, 0 }}, 129 | {{ 11, 10, 0 }, { 11, 0, 3 }, { 10, 5, 0 }, { 8, 0, 7 }, { 5, 7, 0 }}, 130 | {{ 11, 10, 5 }, { 7, 11, 5 }}, 131 | {{ 10, 6, 5 }}, 132 | {{ 0, 8, 3 }, { 5, 10, 6 }}, 133 | {{ 9, 0, 1 }, { 5, 10, 6 }}, 134 | {{ 1, 8, 3 }, { 1, 9, 8 }, { 5, 10, 6 }}, 135 | {{ 1, 6, 5 }, { 2, 6, 1 }}, 136 | {{ 1, 6, 5 }, { 1, 2, 6 }, { 3, 0, 8 }}, 137 | {{ 9, 6, 5 }, { 9, 0, 6 }, { 0, 2, 6 }}, 138 | {{ 5, 9, 8 }, { 5, 8, 2 }, { 5, 2, 6 }, { 3, 2, 8 }}, 139 | {{ 2, 3, 11 }, { 10, 6, 5 }}, 140 | {{ 11, 0, 8 }, { 11, 2, 0 }, { 10, 6, 5 }}, 141 | {{ 0, 1, 9 }, { 2, 3, 11 }, { 5, 10, 6 }}, 142 | {{ 5, 10, 6 }, { 1, 9, 2 }, { 9, 11, 2 }, { 9, 8, 11 }}, 143 | {{ 6, 3, 11 }, { 6, 5, 3 }, { 5, 1, 3 }}, 144 | {{ 0, 8, 11 }, { 0, 11, 5 }, { 0, 5, 1 }, { 5, 11, 6 }}, 145 | {{ 3, 11, 6 }, { 0, 3, 6 }, { 0, 6, 5 }, { 0, 5, 9 }}, 146 | {{ 6, 5, 9 }, { 6, 9, 11 }, { 11, 9, 8 }}, 147 | {{ 5, 10, 6 }, { 4, 7, 8 }}, 148 | {{ 4, 3, 0 }, { 4, 7, 3 }, { 6, 5, 10 }}, 149 | {{ 1, 9, 0 }, { 5, 10, 6 }, { 8, 4, 7 }}, 150 | {{ 10, 6, 5 }, { 1, 9, 7 }, { 1, 7, 3 }, { 7, 9, 4 }}, 151 | {{ 6, 1, 2 }, { 6, 5, 1 }, { 4, 7, 8 }}, 152 | {{ 1, 2, 5 }, { 5, 2, 6 }, { 3, 0, 4 }, { 3, 4, 7 }}, 153 | {{ 8, 4, 7 }, { 9, 0, 5 }, { 0, 6, 5 }, { 0, 2, 6 }}, 154 | {{ 7, 3, 9 }, { 7, 9, 4 }, { 3, 2, 9 }, { 5, 9, 6 }, { 2, 6, 9 }}, 155 | {{ 3, 11, 2 }, { 7, 8, 4 }, { 10, 6, 5 }}, 156 | {{ 5, 10, 6 }, { 4, 7, 2 }, { 4, 2, 0 }, { 2, 7, 11 }}, 157 | {{ 0, 1, 9 }, { 4, 7, 8 }, { 2, 3, 11 }, { 5, 10, 6 }}, 158 | {{ 9, 2, 1 }, { 9, 11, 2 }, { 9, 4, 11 }, { 7, 11, 4 }, { 5, 10, 6 }}, 159 | {{ 8, 4, 7 }, { 3, 11, 5 }, { 3, 5, 1 }, { 5, 11, 6 }}, 160 | {{ 5, 1, 11 }, { 5, 11, 6 }, { 1, 0, 11 }, { 7, 11, 4 }, { 0, 4, 11 }}, 161 | {{ 0, 5, 9 }, { 0, 6, 5 }, { 0, 3, 6 }, { 11, 6, 3 }, { 8, 4, 7 }}, 162 | {{ 6, 5, 9 }, { 6, 9, 11 }, { 4, 7, 9 }, { 7, 11, 9 }}, 163 | {{ 10, 4, 9 }, { 6, 4, 10 }}, 164 | {{ 4, 10, 6 }, { 4, 9, 10 }, { 0, 8, 3 }}, 165 | {{ 10, 0, 1 }, { 10, 6, 0 }, { 6, 4, 0 }}, 166 | {{ 8, 3, 1 }, { 8, 1, 6 }, { 8, 6, 4 }, { 6, 1, 10 }}, 167 | {{ 1, 4, 9 }, { 1, 2, 4 }, { 2, 6, 4 }}, 168 | {{ 3, 0, 8 }, { 1, 2, 9 }, { 2, 4, 9 }, { 2, 6, 4 }}, 169 | {{ 0, 2, 4 }, { 4, 2, 6 }}, 170 | {{ 8, 3, 2 }, { 8, 2, 4 }, { 4, 2, 6 }}, 171 | {{ 10, 4, 9 }, { 10, 6, 4 }, { 11, 2, 3 }}, 172 | {{ 0, 8, 2 }, { 2, 8, 11 }, { 4, 9, 10 }, { 4, 10, 6 }}, 173 | {{ 3, 11, 2 }, { 0, 1, 6 }, { 0, 6, 4 }, { 6, 1, 10 }}, 174 | {{ 6, 4, 1 }, { 6, 1, 10 }, { 4, 8, 1 }, { 2, 1, 11 }, { 8, 11, 1 }}, 175 | {{ 9, 6, 4 }, { 9, 3, 6 }, { 9, 1, 3 }, { 11, 6, 3 }}, 176 | {{ 8, 11, 1 }, { 8, 1, 0 }, { 11, 6, 1 }, { 9, 1, 4 }, { 6, 4, 1 }}, 177 | {{ 3, 11, 6 }, { 3, 6, 0 }, { 0, 6, 4 }}, 178 | {{ 6, 4, 8 }, { 11, 6, 8 }}, 179 | {{ 7, 10, 6 }, { 7, 8, 10 }, { 8, 9, 10 }}, 180 | {{ 0, 7, 3 }, { 0, 10, 7 }, { 0, 9, 10 }, { 6, 7, 10 }}, 181 | {{ 10, 6, 7 }, { 1, 10, 7 }, { 1, 7, 8 }, { 1, 8, 0 }}, 182 | {{ 10, 6, 7 }, { 10, 7, 1 }, { 1, 7, 3 }}, 183 | {{ 1, 2, 6 }, { 1, 6, 8 }, { 1, 8, 9 }, { 8, 6, 7 }}, 184 | {{ 2, 6, 9 }, { 2, 9, 1 }, { 6, 7, 9 }, { 0, 9, 3 }, { 7, 3, 9 }}, 185 | {{ 7, 8, 0 }, { 7, 0, 6 }, { 6, 0, 2 }}, 186 | {{ 7, 3, 2 }, { 6, 7, 2 }}, 187 | {{ 2, 3, 11 }, { 10, 6, 8 }, { 10, 8, 9 }, { 8, 6, 7 }}, 188 | {{ 2, 0, 7 }, { 2, 7, 11 }, { 0, 9, 7 }, { 6, 7, 10 }, { 9, 10, 7 }}, 189 | {{ 1, 8, 0 }, { 1, 7, 8 }, { 1, 10, 7 }, { 6, 7, 10 }, { 2, 3, 11 }}, 190 | {{ 11, 2, 1 }, { 11, 1, 7 }, { 10, 6, 1 }, { 6, 7, 1 }}, 191 | {{ 8, 9, 6 }, { 8, 6, 7 }, { 9, 1, 6 }, { 11, 6, 3 }, { 1, 3, 6 }}, 192 | {{ 0, 9, 1 }, { 11, 6, 7 }}, 193 | {{ 7, 8, 0 }, { 7, 0, 6 }, { 3, 11, 0 }, { 11, 6, 0 }}, 194 | {{ 7, 11, 6 }}, 195 | {{ 7, 6, 11 }}, 196 | {{ 3, 0, 8 }, { 11, 7, 6 }}, 197 | {{ 0, 1, 9 }, { 11, 7, 6 }}, 198 | {{ 8, 1, 9 }, { 8, 3, 1 }, { 11, 7, 6 }}, 199 | {{ 10, 1, 2 }, { 6, 11, 7 }}, 200 | {{ 1, 2, 10 }, { 3, 0, 8 }, { 6, 11, 7 }}, 201 | {{ 2, 9, 0 }, { 2, 10, 9 }, { 6, 11, 7 }}, 202 | {{ 6, 11, 7 }, { 2, 10, 3 }, { 10, 8, 3 }, { 10, 9, 8 }}, 203 | {{ 7, 2, 3 }, { 6, 2, 7 }}, 204 | {{ 7, 0, 8 }, { 7, 6, 0 }, { 6, 2, 0 }}, 205 | {{ 2, 7, 6 }, { 2, 3, 7 }, { 0, 1, 9 }}, 206 | {{ 1, 6, 2 }, { 1, 8, 6 }, { 1, 9, 8 }, { 8, 7, 6 }}, 207 | {{ 10, 7, 6 }, { 10, 1, 7 }, { 1, 3, 7 }}, 208 | {{ 10, 7, 6 }, { 1, 7, 10 }, { 1, 8, 7 }, { 1, 0, 8 }}, 209 | {{ 0, 3, 7 }, { 0, 7, 10 }, { 0, 10, 9 }, { 6, 10, 7 }}, 210 | {{ 7, 6, 10 }, { 7, 10, 8 }, { 8, 10, 9 }}, 211 | {{ 6, 8, 4 }, { 11, 8, 6 }}, 212 | {{ 3, 6, 11 }, { 3, 0, 6 }, { 0, 4, 6 }}, 213 | {{ 8, 6, 11 }, { 8, 4, 6 }, { 9, 0, 1 }}, 214 | {{ 9, 4, 6 }, { 9, 6, 3 }, { 9, 3, 1 }, { 11, 3, 6 }}, 215 | {{ 6, 8, 4 }, { 6, 11, 8 }, { 2, 10, 1 }}, 216 | {{ 1, 2, 10 }, { 3, 0, 11 }, { 0, 6, 11 }, { 0, 4, 6 }}, 217 | {{ 4, 11, 8 }, { 4, 6, 11 }, { 0, 2, 9 }, { 2, 10, 9 }}, 218 | {{ 10, 9, 3 }, { 10, 3, 2 }, { 9, 4, 3 }, { 11, 3, 6 }, { 4, 6, 3 }}, 219 | {{ 8, 2, 3 }, { 8, 4, 2 }, { 4, 6, 2 }}, 220 | {{ 0, 4, 2 }, { 4, 6, 2 }}, 221 | {{ 1, 9, 0 }, { 2, 3, 4 }, { 2, 4, 6 }, { 4, 3, 8 }}, 222 | {{ 1, 9, 4 }, { 1, 4, 2 }, { 2, 4, 6 }}, 223 | {{ 8, 1, 3 }, { 8, 6, 1 }, { 8, 4, 6 }, { 6, 10, 1 }}, 224 | {{ 10, 1, 0 }, { 10, 0, 6 }, { 6, 0, 4 }}, 225 | {{ 4, 6, 3 }, { 4, 3, 8 }, { 6, 10, 3 }, { 0, 3, 9 }, { 10, 9, 3 }}, 226 | {{ 10, 9, 4 }, { 6, 10, 4 }}, 227 | {{ 4, 9, 5 }, { 7, 6, 11 }}, 228 | {{ 0, 8, 3 }, { 4, 9, 5 }, { 11, 7, 6 }}, 229 | {{ 5, 0, 1 }, { 5, 4, 0 }, { 7, 6, 11 }}, 230 | {{ 11, 7, 6 }, { 8, 3, 4 }, { 3, 5, 4 }, { 3, 1, 5 }}, 231 | {{ 9, 5, 4 }, { 10, 1, 2 }, { 7, 6, 11 }}, 232 | {{ 6, 11, 7 }, { 1, 2, 10 }, { 0, 8, 3 }, { 4, 9, 5 }}, 233 | {{ 7, 6, 11 }, { 5, 4, 10 }, { 4, 2, 10 }, { 4, 0, 2 }}, 234 | {{ 3, 4, 8 }, { 3, 5, 4 }, { 3, 2, 5 }, { 10, 5, 2 }, { 11, 7, 6 }}, 235 | {{ 7, 2, 3 }, { 7, 6, 2 }, { 5, 4, 9 }}, 236 | {{ 9, 5, 4 }, { 0, 8, 6 }, { 0, 6, 2 }, { 6, 8, 7 }}, 237 | {{ 3, 6, 2 }, { 3, 7, 6 }, { 1, 5, 0 }, { 5, 4, 0 }}, 238 | {{ 6, 2, 8 }, { 6, 8, 7 }, { 2, 1, 8 }, { 4, 8, 5 }, { 1, 5, 8 }}, 239 | {{ 9, 5, 4 }, { 10, 1, 6 }, { 1, 7, 6 }, { 1, 3, 7 }}, 240 | {{ 1, 6, 10 }, { 1, 7, 6 }, { 1, 0, 7 }, { 8, 7, 0 }, { 9, 5, 4 }}, 241 | {{ 4, 0, 10 }, { 4, 10, 5 }, { 0, 3, 10 }, { 6, 10, 7 }, { 3, 7, 10 }}, 242 | {{ 7, 6, 10 }, { 7, 10, 8 }, { 5, 4, 10 }, { 4, 8, 10 }}, 243 | {{ 6, 9, 5 }, { 6, 11, 9 }, { 11, 8, 9 }}, 244 | {{ 3, 6, 11 }, { 0, 6, 3 }, { 0, 5, 6 }, { 0, 9, 5 }}, 245 | {{ 0, 11, 8 }, { 0, 5, 11 }, { 0, 1, 5 }, { 5, 6, 11 }}, 246 | {{ 6, 11, 3 }, { 6, 3, 5 }, { 5, 3, 1 }}, 247 | {{ 1, 2, 10 }, { 9, 5, 11 }, { 9, 11, 8 }, { 11, 5, 6 }}, 248 | {{ 0, 11, 3 }, { 0, 6, 11 }, { 0, 9, 6 }, { 5, 6, 9 }, { 1, 2, 10 }}, 249 | {{ 11, 8, 5 }, { 11, 5, 6 }, { 8, 0, 5 }, { 10, 5, 2 }, { 0, 2, 5 }}, 250 | {{ 6, 11, 3 }, { 6, 3, 5 }, { 2, 10, 3 }, { 10, 5, 3 }}, 251 | {{ 5, 8, 9 }, { 5, 2, 8 }, { 5, 6, 2 }, { 3, 8, 2 }}, 252 | {{ 9, 5, 6 }, { 9, 6, 0 }, { 0, 6, 2 }}, 253 | {{ 1, 5, 8 }, { 1, 8, 0 }, { 5, 6, 8 }, { 3, 8, 2 }, { 6, 2, 8 }}, 254 | {{ 1, 5, 6 }, { 2, 1, 6 }}, 255 | {{ 1, 3, 6 }, { 1, 6, 10 }, { 3, 8, 6 }, { 5, 6, 9 }, { 8, 9, 6 }}, 256 | {{ 10, 1, 0 }, { 10, 0, 6 }, { 9, 5, 0 }, { 5, 6, 0 }}, 257 | {{ 0, 3, 8 }, { 5, 6, 10 }}, 258 | {{ 10, 5, 6 }}, 259 | {{ 11, 5, 10 }, { 7, 5, 11 }}, 260 | {{ 11, 5, 10 }, { 11, 7, 5 }, { 8, 3, 0 }}, 261 | {{ 5, 11, 7 }, { 5, 10, 11 }, { 1, 9, 0 }}, 262 | {{ 10, 7, 5 }, { 10, 11, 7 }, { 9, 8, 1 }, { 8, 3, 1 }}, 263 | {{ 11, 1, 2 }, { 11, 7, 1 }, { 7, 5, 1 }}, 264 | {{ 0, 8, 3 }, { 1, 2, 7 }, { 1, 7, 5 }, { 7, 2, 11 }}, 265 | {{ 9, 7, 5 }, { 9, 2, 7 }, { 9, 0, 2 }, { 2, 11, 7 }}, 266 | {{ 7, 5, 2 }, { 7, 2, 11 }, { 5, 9, 2 }, { 3, 2, 8 }, { 9, 8, 2 }}, 267 | {{ 2, 5, 10 }, { 2, 3, 5 }, { 3, 7, 5 }}, 268 | {{ 8, 2, 0 }, { 8, 5, 2 }, { 8, 7, 5 }, { 10, 2, 5 }}, 269 | {{ 9, 0, 1 }, { 5, 10, 3 }, { 5, 3, 7 }, { 3, 10, 2 }}, 270 | {{ 9, 8, 2 }, { 9, 2, 1 }, { 8, 7, 2 }, { 10, 2, 5 }, { 7, 5, 2 }}, 271 | {{ 1, 3, 5 }, { 3, 7, 5 }}, 272 | {{ 0, 8, 7 }, { 0, 7, 1 }, { 1, 7, 5 }}, 273 | {{ 9, 0, 3 }, { 9, 3, 5 }, { 5, 3, 7 }}, 274 | {{ 9, 8, 7 }, { 5, 9, 7 }}, 275 | {{ 5, 8, 4 }, { 5, 10, 8 }, { 10, 11, 8 }}, 276 | {{ 5, 0, 4 }, { 5, 11, 0 }, { 5, 10, 11 }, { 11, 3, 0 }}, 277 | {{ 0, 1, 9 }, { 8, 4, 10 }, { 8, 10, 11 }, { 10, 4, 5 }}, 278 | {{ 10, 11, 4 }, { 10, 4, 5 }, { 11, 3, 4 }, { 9, 4, 1 }, { 3, 1, 4 }}, 279 | {{ 2, 5, 1 }, { 2, 8, 5 }, { 2, 11, 8 }, { 4, 5, 8 }}, 280 | {{ 0, 4, 11 }, { 0, 11, 3 }, { 4, 5, 11 }, { 2, 11, 1 }, { 5, 1, 11 }}, 281 | {{ 0, 2, 5 }, { 0, 5, 9 }, { 2, 11, 5 }, { 4, 5, 8 }, { 11, 8, 5 }}, 282 | {{ 9, 4, 5 }, { 2, 11, 3 }}, 283 | {{ 2, 5, 10 }, { 3, 5, 2 }, { 3, 4, 5 }, { 3, 8, 4 }}, 284 | {{ 5, 10, 2 }, { 5, 2, 4 }, { 4, 2, 0 }}, 285 | {{ 3, 10, 2 }, { 3, 5, 10 }, { 3, 8, 5 }, { 4, 5, 8 }, { 0, 1, 9 }}, 286 | {{ 5, 10, 2 }, { 5, 2, 4 }, { 1, 9, 2 }, { 9, 4, 2 }}, 287 | {{ 8, 4, 5 }, { 8, 5, 3 }, { 3, 5, 1 }}, 288 | {{ 0, 4, 5 }, { 1, 0, 5 }}, 289 | {{ 8, 4, 5 }, { 8, 5, 3 }, { 9, 0, 5 }, { 0, 3, 5 }}, 290 | {{ 9, 4, 5 }}, 291 | {{ 4, 11, 7 }, { 4, 9, 11 }, { 9, 10, 11 }}, 292 | {{ 0, 8, 3 }, { 4, 9, 7 }, { 9, 11, 7 }, { 9, 10, 11 }}, 293 | {{ 1, 10, 11 }, { 1, 11, 4 }, { 1, 4, 0 }, { 7, 4, 11 }}, 294 | {{ 3, 1, 4 }, { 3, 4, 8 }, { 1, 10, 4 }, { 7, 4, 11 }, { 10, 11, 4 }}, 295 | {{ 4, 11, 7 }, { 9, 11, 4 }, { 9, 2, 11 }, { 9, 1, 2 }}, 296 | {{ 9, 7, 4 }, { 9, 11, 7 }, { 9, 1, 11 }, { 2, 11, 1 }, { 0, 8, 3 }}, 297 | {{ 11, 7, 4 }, { 11, 4, 2 }, { 2, 4, 0 }}, 298 | {{ 11, 7, 4 }, { 11, 4, 2 }, { 8, 3, 4 }, { 3, 2, 4 }}, 299 | {{ 2, 9, 10 }, { 2, 7, 9 }, { 2, 3, 7 }, { 7, 4, 9 }}, 300 | {{ 9, 10, 7 }, { 9, 7, 4 }, { 10, 2, 7 }, { 8, 7, 0 }, { 2, 0, 7 }}, 301 | {{ 3, 7, 10 }, { 3, 10, 2 }, { 7, 4, 10 }, { 1, 10, 0 }, { 4, 0, 10 }}, 302 | {{ 1, 10, 2 }, { 8, 7, 4 }}, 303 | {{ 4, 9, 1 }, { 4, 1, 7 }, { 7, 1, 3 }}, 304 | {{ 4, 9, 1 }, { 4, 1, 7 }, { 0, 8, 1 }, { 8, 7, 1 }}, 305 | {{ 4, 0, 3 }, { 7, 4, 3 }}, 306 | {{ 4, 8, 7 }}, 307 | {{ 9, 10, 8 }, { 10, 11, 8 }}, 308 | {{ 3, 0, 9 }, { 3, 9, 11 }, { 11, 9, 10 }}, 309 | {{ 0, 1, 10 }, { 0, 10, 8 }, { 8, 10, 11 }}, 310 | {{ 3, 1, 10 }, { 11, 3, 10 }}, 311 | {{ 1, 2, 11 }, { 1, 11, 9 }, { 9, 11, 8 }}, 312 | {{ 3, 0, 9 }, { 3, 9, 11 }, { 1, 2, 9 }, { 2, 11, 9 }}, 313 | {{ 0, 2, 11 }, { 8, 0, 11 }}, 314 | {{ 3, 2, 11 }}, 315 | {{ 2, 3, 8 }, { 2, 8, 10 }, { 10, 8, 9 }}, 316 | {{ 9, 10, 2 }, { 0, 9, 2 }}, 317 | {{ 2, 3, 8 }, { 2, 8, 10 }, { 0, 1, 8 }, { 1, 10, 8 }}, 318 | {{ 1, 10, 2 }}, 319 | {{ 1, 3, 8 }, { 9, 1, 8 }}, 320 | {{ 0, 9, 1 }}, 321 | {{ 0, 3, 8 }}, 322 | {} 323 | }; 324 | } 325 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/mc/MarchingCubesMeshGenerator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.mc; 38 | 39 | import com.jme3.math.Vector3f; 40 | import com.jme3.scene.Mesh; 41 | import com.jme3.scene.VertexBuffer.Type; 42 | import com.jme3.util.BufferUtils; 43 | import com.simsilica.iso.DensityVolume; 44 | import com.simsilica.iso.MeshGenerator; 45 | import java.nio.FloatBuffer; 46 | import java.nio.IntBuffer; 47 | import java.util.ArrayList; 48 | import java.util.Arrays; 49 | import java.util.List; 50 | 51 | 52 | /** 53 | * Takes a density field and generates meshes for it 54 | * using the Marching Cubes algorithm. 55 | * 56 | * @author Paul Speed 57 | */ 58 | public class MarchingCubesMeshGenerator implements MeshGenerator { 59 | 60 | private int cx; 61 | private int cy; 62 | private int cz; 63 | private int[] masks; 64 | private int[] cells; 65 | private int cellIndex = 0; 66 | private int triangleCount = 0; 67 | private int vertCount = 0; 68 | private int maskIndex = 0; 69 | private boolean[] edgeHit = new boolean[12]; 70 | private int[][][][] edgeVerts; 71 | private float xzScale = 1; 72 | 73 | /** 74 | * Creates a Marching Cubes based mesh generator that will 75 | * generate chunks of the specified size. 76 | */ 77 | public MarchingCubesMeshGenerator( int cx, int cy, int cz ) { 78 | this(cx, cy, cz, 1); 79 | } 80 | 81 | /** 82 | * Creates a Marching Cubes based mesh generator that will 83 | * generate chunks of the specified size with an extra x, z 84 | * scale applied to the resulting mesh. In other words, 85 | * even though the source data may be sampled in 64x64 cell 86 | * chunks, with an xzScale of 2 the generated geometry will be 87 | * 128 x 128. 88 | */ 89 | public MarchingCubesMeshGenerator( int cx, int cy, int cz, float xzScale ) { 90 | cx++; 91 | cy++; 92 | cz++; 93 | this.cx = cx; 94 | this.cy = cy; 95 | this.cz = cz; 96 | this.masks = new int[cx * cy * cz]; 97 | this.cells = new int[cx * cy * cz]; 98 | this.edgeVerts = new int[cx+1][cy+1][cz+1][3]; 99 | this.xzScale = xzScale; 100 | } 101 | 102 | public Vector3f getRequiredVolumeSize() { 103 | // When the creator passed in a size of "cells", we actually 104 | // sample corners and so neded +1 105 | // However, we also sample an extra border on all sides which 106 | // is another +2... +3 in all. But the +1 is already incorporated 107 | // into our size fields. 108 | return new Vector3f(cx + 2, cy + 2, cz + 2); 109 | } 110 | 111 | public Vector3f getGenerationSize() { 112 | int x = cx - 1; 113 | int y = cy - 1; 114 | int z = cz - 1; 115 | return new Vector3f(x * xzScale, y, z * xzScale); 116 | } 117 | 118 | public void setXzScale( float s ) { 119 | this.xzScale = s; 120 | } 121 | 122 | public float getXzScale() { 123 | return xzScale; 124 | } 125 | 126 | private int solid( float value ) { 127 | return value > 0 ? 1 : 0; 128 | } 129 | 130 | private Vector3f getEdgePoint( int x, int y, int z, int edge, DensityVolume volume ) { 131 | 132 | int x1 = x + MarchingCubesConstants.edgeStarts[edge][0] + 1; 133 | int y1 = y + MarchingCubesConstants.edgeStarts[edge][1] + 1; 134 | int z1 = z + MarchingCubesConstants.edgeStarts[edge][2] + 1; 135 | float d1 = volume.getDensity(x1, y1, z1); 136 | 137 | int x2 = x + MarchingCubesConstants.edgeEnds[edge][0] + 1; 138 | int y2 = y + MarchingCubesConstants.edgeEnds[edge][1] + 1; 139 | int z2 = z + MarchingCubesConstants.edgeEnds[edge][2] + 1; 140 | float d2 = volume.getDensity(x2, y2, z2); 141 | 142 | // If d1 is -0.2 and d2 is 0.6 then the 143 | // point should be 0.25 from edge start. 144 | float part = Math.abs(d1) / Math.abs(d2 - d1); 145 | float vx = x1 + (x2-x1) * part; 146 | float vy = y1 + (y2-y1) * part; 147 | float vz = z1 + (z2-z1) * part; 148 | Vector3f vert = new Vector3f(vx, vy, vz); 149 | return vert; 150 | } 151 | 152 | /** 153 | * Builds a mesh from the specified volume. The resulting mesh 154 | * will be extracted from 0 to size in all directions but requires 155 | * the volume to support queries -1 to size + 2 because it will 156 | * will internally build a border of cells. 157 | */ 158 | public Mesh buildMesh( DensityVolume volume ) { 159 | 160 | List verts = new ArrayList(); 161 | List normals = new ArrayList(); 162 | cellIndex = 0; 163 | triangleCount = 0; 164 | vertCount = 0; 165 | maskIndex = 0; 166 | 167 | // Build up the edge indexes so we can share edges 168 | for( int x = 0; x < cx; x++ ) { 169 | for( int y = 0; y < cy; y++ ) { 170 | for( int z = 0; z < cz; z++ ) { 171 | int bits = 0; 172 | int sx = x + 1; 173 | int sy = y + 1; 174 | int sz = z + 1; 175 | 176 | bits |= solid(volume.getDensity(sx , sy , sz )); 177 | bits |= solid(volume.getDensity(sx , sy+1, sz )) << 1; 178 | bits |= solid(volume.getDensity(sx+1, sy+1, sz )) << 2; 179 | bits |= solid(volume.getDensity(sx+1, sy , sz )) << 3; 180 | bits |= solid(volume.getDensity(sx , sy , sz+1)) << 4; 181 | bits |= solid(volume.getDensity(sx , sy+1, sz+1)) << 5; 182 | bits |= solid(volume.getDensity(sx+1, sy+1, sz+1)) << 6; 183 | bits |= solid(volume.getDensity(sx+1, sy , sz+1)) << 7; 184 | 185 | if( MarchingCubesConstants.triEdges[bits].length > 0 ) { 186 | // We _do_ want to process some of the edges but 187 | // we _don't_ want to process the actual cell if 188 | // it is the outside border 189 | if( x < cx - 1 && y < cy - 1 && z < cz - 1 ) { 190 | cells[cellIndex++] = maskIndex; 191 | } 192 | int[][] triangles = MarchingCubesConstants.triEdges[bits]; 193 | triangleCount += triangles.length; 194 | Arrays.fill(edgeHit, false); 195 | 196 | for( int t = 0; t < triangles.length; t++ ) { 197 | int[] triEdges = triangles[t]; 198 | 199 | for( int i = 0; i < 3; i++ ) { 200 | edgeHit[triEdges[i]] = true; 201 | } 202 | } 203 | 204 | // The density field is theoretically cx * cy * cz which 205 | // means that we only need to generate cells for cx - 1, cy -1, cz -1 206 | // We generate extra cells to make sure we have the edges we need... 207 | // but these cells are technically outside of the field and 208 | // we will need to skip them later. Also, I guess we can avoid 209 | // generating some extra vertexes that would stick out. 210 | if( edgeHit[0] && y < cy - 1 ) { 211 | Vector3f vert = getEdgePoint(x, y, z, 0, volume); 212 | edgeVerts[x][y][z][0] = verts.size(); 213 | verts.add(vert); 214 | normals.add(volume.getFieldDirection(vert.x, vert.y, vert.z, null)); 215 | vert.x--; 216 | vert.y--; 217 | vert.z--; 218 | } 219 | if( edgeHit[3] && x < cx - 1 ) { 220 | Vector3f vert = getEdgePoint(x, y, z, 3, volume); 221 | edgeVerts[x][y][z][1] = verts.size(); 222 | verts.add(vert); 223 | normals.add(volume.getFieldDirection(vert.x, vert.y, vert.z, null)); 224 | vert.x--; 225 | vert.y--; 226 | vert.z--; 227 | } 228 | if( edgeHit[8] && z < cz - 1 ) { 229 | Vector3f vert = getEdgePoint(x, y, z, 8, volume); 230 | edgeVerts[x][y][z][2] = verts.size(); 231 | verts.add(vert); 232 | normals.add(volume.getFieldDirection(vert.x, vert.y, vert.z, null)); 233 | vert.x--; 234 | vert.y--; 235 | vert.z--; 236 | } 237 | } 238 | masks[maskIndex++] = bits; 239 | } 240 | } 241 | } 242 | 243 | int cellCount = cellIndex; 244 | if( cellCount == 0 ) 245 | return null; 246 | 247 | if( xzScale != 1 ) { 248 | for( int i = 0; i < verts.size(); i++ ) { 249 | Vector3f v = verts.get(i); 250 | Vector3f n = normals.get(i); 251 | v.x *= xzScale; 252 | v.z *= xzScale; 253 | n.y *= xzScale; 254 | n.normalizeLocal(); 255 | } 256 | } 257 | 258 | int[] triIndexes = new int[triangleCount * 3]; 259 | int triIndexIndex = 0; 260 | 261 | // Now let's just visit the non-empty cells and spin out the 262 | // shared triangles' indexes. 263 | int cycz = cy * cz; 264 | for( int c = 0; c < cellCount; c++ ) { 265 | 266 | int index = cells[c]; 267 | int mask = masks[index]; 268 | int x = index / (cycz); 269 | int y = (index % (cycz)) / cz; 270 | int z = index % cz; 271 | 272 | int[][] triangles = MarchingCubesConstants.triEdges[mask]; 273 | if( triangles.length == 0 ) { 274 | throw new RuntimeException("Algorithm inconsistency detected."); 275 | //System.out.println( "****** How did this happen? ******" ); 276 | //continue; 277 | } 278 | 279 | for( int t = 0; t < triangles.length; t++ ) { 280 | 281 | int[] triEdges = triangles[t]; 282 | 283 | for( int i = 0; i < 3; i++ ) { 284 | int vertIndex; 285 | switch(triEdges[i]) { 286 | case 0: 287 | vertIndex = edgeVerts[x][y][z][0]; 288 | break; 289 | case 1: 290 | vertIndex = edgeVerts[x][y+1][z][1]; 291 | break; 292 | case 2: 293 | vertIndex = edgeVerts[x+1][y][z][0]; 294 | break; 295 | case 3: 296 | vertIndex = edgeVerts[x][y][z][1]; 297 | break; 298 | case 4: 299 | vertIndex = edgeVerts[x][y][z+1][0]; 300 | break; 301 | case 5: 302 | vertIndex = edgeVerts[x][y+1][z+1][1]; 303 | break; 304 | case 6: 305 | vertIndex = edgeVerts[x+1][y][z+1][0]; 306 | break; 307 | case 7: 308 | vertIndex = edgeVerts[x][y][z+1][1]; 309 | break; 310 | case 8: 311 | vertIndex = edgeVerts[x][y][z][2]; 312 | break; 313 | case 9: 314 | vertIndex = edgeVerts[x][y+1][z][2]; 315 | break; 316 | case 10: 317 | vertIndex = edgeVerts[x+1][y+1][z][2]; 318 | break; 319 | case 11: 320 | vertIndex = edgeVerts[x+1][y][z][2]; 321 | break; 322 | default: 323 | throw new RuntimeException("Unknown edge:" + triEdges[i]); 324 | } 325 | 326 | triIndexes[triIndexIndex++] = vertIndex; 327 | } 328 | } 329 | } 330 | 331 | Vector3f[] vertArray = new Vector3f[verts.size()]; 332 | vertArray = verts.toArray(vertArray); 333 | 334 | Vector3f[] normArray = new Vector3f[normals.size()]; 335 | normArray = normals.toArray(normArray); 336 | 337 | Mesh mesh = new Mesh(); 338 | FloatBuffer pb = BufferUtils.createFloatBuffer(vertArray); 339 | mesh.setBuffer(Type.Position, 3, pb); 340 | FloatBuffer nb = BufferUtils.createFloatBuffer(normArray); 341 | mesh.setBuffer(Type.Normal, 3, nb); 342 | IntBuffer ib = BufferUtils.createIntBuffer(triIndexes); 343 | mesh.setBuffer(Type.Index, 3, ib); 344 | 345 | mesh.updateBound(); 346 | 347 | return mesh; 348 | } 349 | } 350 | 351 | 352 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/plot/BatchInstance.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.plot; 38 | 39 | import com.jme3.math.Quaternion; 40 | import com.jme3.math.Vector3f; 41 | 42 | 43 | /** 44 | * Holds the information about a single object's 45 | * transform. The batcher will use these to create the 46 | * batch from the template mesh. 47 | * 48 | * @author Paul Speed 49 | */ 50 | public class BatchInstance { 51 | public Vector3f position; 52 | public Quaternion rotation; 53 | public float scale; 54 | 55 | public BatchInstance() { 56 | } 57 | 58 | public BatchInstance( Vector3f pos, Quaternion rot, float scale ) { 59 | this.position = pos; 60 | this.rotation = rot; 61 | this.scale = scale; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/plot/InstanceTemplate.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.plot; 38 | 39 | import com.jme3.bounding.BoundingBox; 40 | import com.jme3.math.Matrix4f; 41 | import com.jme3.math.Quaternion; 42 | import com.jme3.math.Vector3f; 43 | import com.jme3.scene.Geometry; 44 | import com.jme3.scene.Mesh; 45 | import com.jme3.scene.VertexBuffer; 46 | import com.jme3.scene.VertexBuffer.Format; 47 | import com.jme3.scene.VertexBuffer.Type; 48 | import com.jme3.scene.VertexBuffer.Usage; 49 | import com.simsilica.iso.util.MatrixUtils; 50 | import java.nio.FloatBuffer; 51 | import java.util.List; 52 | 53 | 54 | /** 55 | * Wraps a Geometry to provide a template for 56 | * instance-batching. This could have been combined with 57 | * BatchTemplate but I'm trying to isolated the JME 3.x specific 58 | * dependencies from the JME 3.0 dependencies. 59 | * 60 | * @author Paul Speed 61 | */ 62 | public class InstanceTemplate { 63 | private Geometry sourceGeom; 64 | private Mesh source; 65 | private VertexBuffer[] templates; 66 | private boolean intIndex; 67 | 68 | public InstanceTemplate( Geometry sourceGeom, boolean intIndex ) { 69 | this.sourceGeom = sourceGeom; 70 | this.intIndex = intIndex; 71 | this.source = sourceGeom.getMesh(); 72 | } 73 | 74 | public Geometry createInstances( List instances ) { 75 | if( instances.isEmpty() ) { 76 | return null; 77 | } 78 | 79 | Vector3f offset = sourceGeom.getWorldTranslation(); 80 | 81 | // For instances, we just clone the original mesh and add 82 | // in the transform instance data 83 | Mesh mesh = source.clone(); 84 | 85 | Vector3f min = new Vector3f(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY); 86 | Vector3f max = new Vector3f(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY); 87 | Matrix4f[] transforms = new Matrix4f[instances.size()]; 88 | for( int i = 0; i < instances.size(); i++ ) { 89 | BatchInstance instance = instances.get(i); 90 | 91 | Vector3f p1 = instance.position; 92 | Quaternion rot = instance.rotation; 93 | float scale = instance.scale; 94 | 95 | // The offset needs to be projected 96 | Vector3f localOffset = rot.mult(offset); 97 | localOffset.multLocal(scale); 98 | 99 | p1 = p1.add(localOffset); 100 | 101 | min.minLocal(p1); 102 | max.maxLocal(p1); 103 | 104 | 105 | Matrix4f transform = new Matrix4f(); 106 | transform.setRotationQuaternion(rot); 107 | transform.setTranslation(p1); 108 | transform.setScale(scale, scale, scale); 109 | 110 | transforms[i] = transform; 111 | } 112 | 113 | // Note: this doesn't calculate it correctly right now 114 | // so we'll do it manually and fudge it a little. 115 | //mesh.updateBound(); 116 | min.subtractLocal(3, 1, 3); 117 | max.addLocal(3, 8, 3); 118 | BoundingBox bounds = new BoundingBox(min, max); 119 | 120 | // Create the transform buffer 121 | FloatBuffer xb = MatrixUtils.createMatrixBuffer(transforms); 122 | 123 | VertexBuffer vb = new VertexBuffer(Type.InstanceData); 124 | vb.setInstanceSpan(1); 125 | vb.setupData(Usage.Stream, 16, Format.Float, xb); 126 | 127 | return createInstances(vb, bounds); 128 | } 129 | 130 | public Geometry createInstances( VertexBuffer transforms, BoundingBox bounds ) { 131 | 132 | // For instances, we just clone the original mesh and add 133 | // in the transform instance data 134 | Mesh mesh = source.clone(); 135 | mesh.setBuffer(transforms); 136 | mesh.setBound(bounds); 137 | 138 | Geometry result = new Geometry("batch:" + sourceGeom.getName(), mesh); 139 | result.setMaterial(sourceGeom.getMaterial()); 140 | result.setQueueBucket(sourceGeom.getQueueBucket()); 141 | return result; 142 | } 143 | 144 | 145 | 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/tri/Triangle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.tri; 38 | 39 | import com.jme3.math.Vector2f; 40 | import com.jme3.math.Vector3f; 41 | 42 | 43 | /** 44 | * 45 | * 46 | * @author Paul Speed 47 | */ 48 | public class Triangle implements Cloneable { 49 | 50 | public Vector3f[] verts = { new Vector3f(), new Vector3f(), new Vector3f() }; 51 | public Vector3f[] norms = { new Vector3f(), new Vector3f(), new Vector3f() }; 52 | public Vector2f[] texes = { new Vector2f(), new Vector2f(), new Vector2f() }; 53 | public int[] indexes = new int[3]; 54 | 55 | public Triangle() { 56 | } 57 | 58 | @Override 59 | public Triangle clone() { 60 | Triangle result = new Triangle(); 61 | for( int v = 0; v < 3; v++ ) { 62 | result.verts[v].set(verts[v]); 63 | result.norms[v].set(norms[v]); 64 | result.texes[v].set(texes[v]); 65 | result.indexes[v] = indexes[v]; 66 | } 67 | return result; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "Triangle[" + verts[0] + ", " + verts[1] + ", " + verts[2] + "]"; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/tri/TriangleProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.tri; 38 | 39 | import com.jme3.scene.Mesh; 40 | 41 | 42 | /** 43 | * 44 | * 45 | * @author Paul Speed 46 | */ 47 | public interface TriangleProcessor { 48 | public void processTriangle( Mesh mesh, int index, Triangle tri ); 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/tri/TriangleUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.tri; 38 | 39 | import com.jme3.math.Vector2f; 40 | import com.jme3.math.Vector3f; 41 | import com.jme3.scene.Geometry; 42 | import com.jme3.scene.Mesh; 43 | import com.jme3.scene.Node; 44 | import com.jme3.scene.Spatial; 45 | import com.jme3.scene.VertexBuffer; 46 | import com.jme3.scene.VertexBuffer.Type; 47 | import com.jme3.scene.mesh.IndexBuffer; 48 | import java.nio.FloatBuffer; 49 | import org.slf4j.Logger; 50 | import org.slf4j.LoggerFactory; 51 | 52 | 53 | /** 54 | * 55 | * 56 | * @author Paul Speed 57 | */ 58 | public class TriangleUtils { 59 | 60 | static Logger log = LoggerFactory.getLogger(TriangleUtils.class); 61 | 62 | public static int processTriangles( Mesh mesh, TriangleProcessor proc ) { 63 | if( log.isTraceEnabled() ) { 64 | log.trace("processTriangles(" + mesh + ", " + proc + ")"); 65 | log.trace(" verts:" + mesh.getVertexCount() + " mode:" + mesh.getMode()); 66 | } 67 | if( mesh.getVertexCount() == 0 ) { 68 | return 0; 69 | } 70 | switch( mesh.getMode() ) { 71 | case LineLoop: 72 | case Lines: 73 | case LineStrip: 74 | case Points: 75 | return 0; // no triangles in those 76 | case Hybrid: 77 | case TriangleFan: 78 | case TriangleStrip: 79 | throw new UnsupportedOperationException("Mesh type not yet supported:" + mesh.getMode()); 80 | case Triangles: 81 | if( mesh.getIndexBuffer() != null ) { 82 | return doIndexedTriangles(mesh, proc); 83 | } else { 84 | return doTriangles(mesh, proc); 85 | } 86 | default: 87 | return 0; 88 | } 89 | } 90 | 91 | public static int processTriangles( Spatial s, TriangleProcessor proc ) { 92 | if( log.isTraceEnabled() ) { 93 | log.trace("processTriangles(Spatial:" + s + ", proc:" + proc + ")"); 94 | } 95 | int count = 0; 96 | if( s instanceof Node ) { 97 | for( Spatial child : ((Node)s).getChildren() ) { 98 | count += processTriangles(child, proc); 99 | } 100 | } else if( s instanceof Geometry ) { 101 | count += processTriangles(((Geometry)s).getMesh(), proc); 102 | } else { 103 | throw new UnsupportedOperationException("Unsupported spatial type:" + s); 104 | } 105 | return count; 106 | } 107 | 108 | protected static int doTriangles( Mesh mesh, TriangleProcessor proc ) { 109 | throw new UnsupportedOperationException("Non-indexed triangles not yet supported."); 110 | } 111 | 112 | protected static int doIndexedTriangles( Mesh mesh, TriangleProcessor proc ) { 113 | Triangle tri = new Triangle(); 114 | 115 | VertexBuffer vbPos = mesh.getBuffer(Type.Position); 116 | int posSize = vbPos.getNumComponents(); 117 | VertexBuffer vbNorms = mesh.getBuffer(Type.Normal); 118 | int normSize = 0; 119 | VertexBuffer vbTexes = mesh.getBuffer(Type.TexCoord); 120 | int texesSize = 0; 121 | 122 | FloatBuffer pos = ((FloatBuffer)vbPos.getData()).duplicate(); 123 | FloatBuffer norms = null; 124 | if( vbNorms != null ) { 125 | norms = ((FloatBuffer)vbNorms.getData()).duplicate(); 126 | normSize = vbNorms.getNumComponents(); 127 | } 128 | FloatBuffer texes = null; 129 | if( vbTexes != null ) { 130 | texes = ((FloatBuffer)vbTexes.getData()).duplicate(); 131 | texesSize = vbTexes.getNumComponents(); 132 | } 133 | 134 | IndexBuffer ib = mesh.getIndexBuffer(); 135 | int size = ib.size(); 136 | int triangleIndex = 0; 137 | for( int i = 0; i < size; ) { 138 | for( int v = 0; v < 3; v++ ) { 139 | int index = ib.get(i++); 140 | tri.indexes[v] = index; 141 | 142 | pos.position(index * posSize); 143 | Vector3f vert = tri.verts[v]; 144 | vert.x = pos.get(); 145 | vert.y = pos.get(); 146 | vert.z = pos.get(); 147 | if( norms != null ) { 148 | norms.position(index * normSize); 149 | Vector3f norm = tri.norms[v]; 150 | norm.x = norms.get(); 151 | norm.y = norms.get(); 152 | norm.z = norms.get(); 153 | } 154 | if( texes != null ) { 155 | texes.position(index * texesSize); 156 | Vector2f tex = tri.texes[v]; 157 | tex.x = texes.get(); 158 | tex.y = texes.get(); 159 | } 160 | } 161 | proc.processTriangle(mesh, triangleIndex++, tri); 162 | } 163 | return triangleIndex; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/util/BilinearArray.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.util; 38 | 39 | 40 | import com.jme3.texture.Image; 41 | import com.jme3.texture.Image.Format; 42 | import com.jme3.texture.Texture; 43 | import java.nio.ByteBuffer; 44 | import org.slf4j.Logger; 45 | import org.slf4j.LoggerFactory; 46 | 47 | 48 | /** 49 | * Array data that can be sampled with bilinear interpolation 50 | * similar to the way textures are sampled. This is an 'int' 51 | * array but the 4 bytes in each int are interpolated separately 52 | * as if they represent a four component color. 53 | * 54 | * @author Paul Speed 55 | */ 56 | public class BilinearArray { 57 | 58 | static Logger log = LoggerFactory.getLogger(BilinearArray.class); 59 | 60 | private int width; 61 | private int height; 62 | private int[] array; 63 | 64 | public BilinearArray( int width, int height ) { 65 | this.width = width; 66 | this.height = height; 67 | this.array = new int[width * height]; 68 | } 69 | 70 | public static BilinearArray fromTexture( Texture texture ) { 71 | return fromImage(texture.getImage()); 72 | } 73 | 74 | public static BilinearArray fromImage( Image image ) { 75 | BilinearArray result = new BilinearArray(image.getWidth(), image.getHeight()); 76 | 77 | ByteBuffer data = image.getData(0); 78 | data.rewind(); 79 | 80 | //System.out.println( "Format:" + image.getFormat() + " bpp:" + image.getFormat().getBitsPerPixel() ); 81 | Format format = image.getFormat(); 82 | int span = image.getFormat().getBitsPerPixel() / 8; 83 | int size = result.array.length; 84 | 85 | for( int i = 0; i < size; i++ ) { 86 | byte b1 = data.get(); 87 | byte b2 = 0; 88 | byte b3 = 0; 89 | byte b4 = 0; 90 | if( span > 1 ) { 91 | b2 = data.get(); 92 | } 93 | if( span > 2 ) { 94 | b3 = data.get(); 95 | } 96 | if( span > 3 ) { 97 | b4 = data.get(); 98 | } 99 | if( span > 4 ) { 100 | for( int skip = 4; skip < span; skip++ ) { 101 | data.get(); 102 | } 103 | } 104 | 105 | int value = 0; 106 | switch( format ) { 107 | case BGR8: 108 | value |= (b3 & 0xff) << 24; 109 | value |= (b2 & 0xff) << 16; 110 | value |= (b1 & 0xff) << 8; 111 | break; 112 | case ABGR8: 113 | value |= (b4 & 0xff) << 24; 114 | value |= (b3 & 0xff) << 16; 115 | value |= (b2 & 0xff) << 8; 116 | value |= (b1 & 0xff); 117 | break; 118 | default : 119 | // Default to rgb or rgba 120 | value |= (b1 & 0xff) << 24; 121 | value |= (b2 & 0xff) << 16; 122 | value |= (b3 & 0xff) << 8; 123 | value |= (b4 & 0xff); 124 | break; 125 | } 126 | result.array[i] = value; 127 | } 128 | 129 | return result; 130 | } 131 | 132 | private int index( int x, int y ) { 133 | return y * width + x; 134 | } 135 | 136 | private int wrappedIndex( int x, int y ) { 137 | x = x % width; 138 | y = y % height; 139 | 140 | if( x < 0 ) { 141 | x = width + x; 142 | } 143 | if( y < 0 ) { 144 | y = height + y; 145 | } 146 | int result = y * width + x; 147 | if( result >= array.length ) { 148 | throw new RuntimeException( "Bad clipping:" + x + ", " + y + " width:" + width + " height:" + height ); 149 | } 150 | return result; 151 | } 152 | 153 | public int get( int x, int y ) { 154 | return array[wrappedIndex(x,y)]; 155 | } 156 | 157 | public void set( int x, int y, int value ) { 158 | array[wrappedIndex(x,y)] = value; 159 | } 160 | 161 | public byte[] getHomogenous( double u, double v, byte[] target ) { 162 | return get(u * width, v * height, target); 163 | } 164 | 165 | public byte[] get( double x, double y, byte[] target ) { 166 | if( target == null ) { 167 | target = new byte[4]; 168 | } 169 | 170 | int ll = get((int)Math.floor(x), (int)Math.floor(y)); 171 | int lr = get((int)Math.ceil(x), (int)Math.floor(y)); 172 | int ul = get((int)Math.floor(x), (int)Math.ceil(y)); 173 | int ur = get((int)Math.ceil(x), (int)Math.ceil(y)); 174 | 175 | if( log.isTraceEnabled() ) { 176 | log.trace("ll:%08X lr:%08X ul:%08X ur:%08X\n", ll, lr, ul, ur); 177 | } 178 | double xPart = x - Math.floor(x); 179 | 180 | int l1 = interpolate(ll & 0xff, lr & 0xff, xPart); 181 | int l2 = interpolate((ll >> 8 ) & 0xff, (lr >> 8 ) & 0xff, xPart); 182 | int l3 = interpolate((ll >> 16) & 0xff, (lr >> 16) & 0xff, xPart); 183 | int l4 = interpolate((ll >> 24) & 0xff, (lr >> 24) & 0xff, xPart); 184 | 185 | int u1 = interpolate(ul & 0xff, ur & 0xff, xPart); 186 | int u2 = interpolate((ul >> 8 ) & 0xff, (ur >> 8 ) & 0xff, xPart); 187 | int u3 = interpolate((ul >> 16) & 0xff, (ur >> 16) & 0xff, xPart); 188 | int u4 = interpolate((ul >> 24) & 0xff, (ur >> 24) & 0xff, xPart); 189 | 190 | double yPart = y - Math.floor(y); 191 | 192 | if( log.isTraceEnabled() ) { 193 | log.trace("lower: %02X%02X%02X%02X upper: %02X%02X%02X%02X\n", l4, l3, l2, l1, u4, u3, u2, u1 ); 194 | } 195 | 196 | target[3] = (byte)interpolate(l1, u1, yPart); 197 | target[2] = (byte)interpolate(l2, u2, yPart); 198 | target[1] = (byte)interpolate(l3, u3, yPart); 199 | target[0] = (byte)interpolate(l4, u4, yPart); 200 | 201 | return target; 202 | } 203 | 204 | private int interpolate( int b1, int b2, double f ) { 205 | int result = b1 + (int)Math.round((b2 - b1) * f); 206 | return result; 207 | } 208 | 209 | public static String toString( byte[] array ) { 210 | return String.format("0x%02X%02X%02X%02X", array[0], array[1], array[2], array[3]); 211 | } 212 | 213 | public static void main( String... args ) { 214 | 215 | BilinearArray test = new BilinearArray(10, 10); 216 | test.set(0, 0, 0x01010404); 217 | test.set(1, 0, 0x08080404); 218 | test.set(0, 1, 0x02020202); 219 | test.set(1, 1, 0x08080808); 220 | 221 | System.out.println( "0,0=" + test.get(0,0) ); 222 | System.out.println( "1,1=" + test.get(1,1) ); 223 | System.out.println( "0.5,0.5=" + toString(test.get(0.5,0.5, null)) ); 224 | System.out.println( "0.25,0.5=" + toString(test.get(0.25,0.5, null)) ); 225 | System.out.println( "0.25,0.25=" + toString(test.get(0.25,0.25, null)) ); 226 | System.out.println( "10,10=" + toString(test.get(10,10, null)) ); 227 | System.out.println( "9.5,9.5=" + toString(test.get(9.5,9.5, null)) ); 228 | System.out.println( "9.25,9.25=" + toString(test.get(9.25,9.25, null)) ); 229 | } 230 | 231 | @Override 232 | public String toString() { 233 | return "BilinearArray[" + width + ", " + height + "]"; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/util/MatrixUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.util; 38 | 39 | import com.jme3.math.Matrix3f; 40 | import com.jme3.math.Matrix4f; 41 | import com.jme3.math.Quaternion; 42 | import com.jme3.util.BufferUtils; 43 | import java.nio.FloatBuffer; 44 | 45 | 46 | /** 47 | * 48 | * 49 | * @author Paul Speed 50 | */ 51 | public class MatrixUtils { 52 | 53 | public static FloatBuffer createMatrixBuffer( int count ) { 54 | return BufferUtils.createFloatBuffer(count * 16); 55 | } 56 | 57 | public static FloatBuffer createMatrixBuffer( Matrix4f[] mats ) { 58 | FloatBuffer result = createMatrixBuffer(mats.length); 59 | 60 | Matrix3f rotMat = new Matrix3f(); 61 | Quaternion rot = new Quaternion(); 62 | 63 | for( Matrix4f mat : mats ) { 64 | mat.toRotationMatrix(rotMat); 65 | rot.fromRotationMatrix(rotMat); 66 | 67 | result.put(mat.m00); 68 | result.put(mat.m10); 69 | result.put(mat.m20); 70 | result.put(rot.getX()); 71 | result.put(mat.m01); 72 | result.put(mat.m11); 73 | result.put(mat.m21); 74 | result.put(rot.getY()); 75 | result.put(mat.m02); 76 | result.put(mat.m12); 77 | result.put(mat.m22); 78 | result.put(rot.getZ()); 79 | result.put(mat.m03); 80 | result.put(mat.m13); 81 | result.put(mat.m23); 82 | result.put(rot.getW()); 83 | } 84 | 85 | result.flip(); 86 | return result; 87 | } 88 | } 89 | 90 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/util/MeshCompareUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.util; 38 | 39 | import com.jme3.scene.Mesh; 40 | import com.jme3.scene.VertexBuffer; 41 | import com.jme3.scene.VertexBuffer.Type; 42 | import java.nio.FloatBuffer; 43 | import java.nio.IntBuffer; 44 | import java.nio.ShortBuffer; 45 | import org.slf4j.Logger; 46 | import org.slf4j.LoggerFactory; 47 | 48 | 49 | /** 50 | * Utility methods for comparing meshes and mesh parts. 51 | * 52 | * @author Paul Speed 53 | */ 54 | public class MeshCompareUtil { 55 | 56 | static Logger log = LoggerFactory.getLogger(MeshCompareUtil.class); 57 | 58 | public static boolean compare( Mesh mesh1, Mesh mesh2 ) { 59 | VertexBuffer[] source = mesh1.getBufferList().getArray(); 60 | for( int i = 0; i < source.length; i++ ) { 61 | VertexBuffer comp = mesh2.getBuffer(source[i].getBufferType()); 62 | if( comp == null ) { 63 | log.info("Second mesh has no type:" + source[i].getBufferType()); 64 | return false; 65 | } 66 | if( !compare(source[i], comp) ) { 67 | return false; 68 | } 69 | } 70 | return true; 71 | } 72 | 73 | public static boolean compare( VertexBuffer vb1, VertexBuffer vb2 ) { 74 | if( vb1.getBufferType() != vb2.getBufferType() ) { 75 | log.info("Buffer types differ 1:" + vb1.getBufferType() + " 2:" + vb2.getBufferType()); 76 | return false; 77 | } 78 | if( vb1.getFormat() != vb2.getFormat() ) { 79 | log.info("Buffer formats differ for type:" + vb1.getBufferType() 80 | + " 1:" + vb1.getFormat() + " 2:" + vb2.getFormat()); 81 | return false; 82 | } 83 | if( vb1.getNumComponents() != vb2.getNumComponents() ) { 84 | log.info("Buffer components differ for type:" + vb1.getBufferType() 85 | + " 1:" + vb1.getNumComponents() + " 2:" + vb2.getNumComponents()); 86 | return false; 87 | } 88 | switch( vb1.getFormat() ) { 89 | case Float: 90 | if( !compare(vb1.getBufferType(), (FloatBuffer)vb1.getData(), (FloatBuffer)vb2.getData()) ) { 91 | return false; 92 | } 93 | break; 94 | case UnsignedShort: 95 | case Short: 96 | if( !compare(vb1.getBufferType(), (ShortBuffer)vb1.getData(), (ShortBuffer)vb2.getData()) ) { 97 | return false; 98 | } 99 | break; 100 | case UnsignedInt: 101 | case Int: 102 | if( !compare(vb1.getBufferType(), (IntBuffer)vb1.getData(), (IntBuffer)vb2.getData()) ) { 103 | return false; 104 | } 105 | break; 106 | default: 107 | throw new UnsupportedOperationException("Unhandled format" + vb1.getFormat()); 108 | } 109 | return true; 110 | } 111 | 112 | public static boolean compare( Type type, FloatBuffer b1, FloatBuffer b2 ) { 113 | b1.rewind(); 114 | b2.rewind(); 115 | for( int i = 0; i < b1.limit(); i++ ) { 116 | float v1 = b1.get(); 117 | float v2 = b2.get(); 118 | if( v1 != v2 ) { 119 | log.info("Type:" + type + " differs at pos:" + i + " 1:" + v1 + " 2:" + v2); 120 | return false; 121 | } 122 | } 123 | return true; 124 | } 125 | 126 | public static boolean compare( Type type, ShortBuffer b1, ShortBuffer b2 ) { 127 | b1.rewind(); 128 | b2.rewind(); 129 | for( int i = 0; i < b1.limit(); i++ ) { 130 | short v1 = b1.get(); 131 | short v2 = b2.get(); 132 | if( v1 != v2 ) { 133 | log.info("Type:" + type + " differs at pos:" + i + " 1:" + v1 + " 2:" + v2); 134 | return false; 135 | } 136 | } 137 | return true; 138 | } 139 | 140 | public static boolean compare( Type type, IntBuffer b1, IntBuffer b2 ) { 141 | b1.rewind(); 142 | b2.rewind(); 143 | for( int i = 0; i < b1.limit(); i++ ) { 144 | int v1 = b1.get(); 145 | int v2 = b2.get(); 146 | if( v1 != v2 ) { 147 | log.info("Type:" + type + " differs at pos:" + i + " 1:" + v1 + " 2:" + v2); 148 | return false; 149 | } 150 | } 151 | return true; 152 | } 153 | 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/volume/ArrayDensityVolume.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.volume; 38 | 39 | import com.jme3.math.Vector3f; 40 | import com.simsilica.iso.DensityVolume; 41 | import java.util.Arrays; 42 | 43 | 44 | /** 45 | * A DensityVolume implementation backed by a fixed size 46 | * array of values. These may be set directly or extracted 47 | * from an existing DensityField as required. Intercell 48 | * sampling is done using trilinear interpolation. 49 | * 50 | * @author Paul Speed 51 | */ 52 | public class ArrayDensityVolume implements DensityVolume { 53 | 54 | private int cx, cy, cz, cLayer; 55 | private float[] array; 56 | 57 | public ArrayDensityVolume( int width, int height, int depth ) { 58 | this.array = new float[width * height * depth]; 59 | this.cx = width; 60 | this.cy = height; 61 | this.cz = depth; 62 | this.cLayer = cx * cy; 63 | } 64 | 65 | public static ArrayDensityVolume create( float[][][] source, 66 | int width, int height, int depth ) { 67 | ArrayDensityVolume result = new ArrayDensityVolume(width, height, depth); 68 | int index = 0; 69 | float[] target = result.array; 70 | for( int z = 0; z < depth; z++ ) { 71 | for( int y = 0; y < height; y++ ) { 72 | for( int x = 0; x < width; x++ ) { 73 | target[index++] = source[x][y][z]; 74 | } 75 | } 76 | } 77 | return result; 78 | } 79 | 80 | public static ArrayDensityVolume extractVolume( DensityVolume source, 81 | int xBase, int yBase, int zBase, 82 | int width, int height, int depth ) { 83 | ArrayDensityVolume result = new ArrayDensityVolume(width, height, depth); 84 | int index = 0; 85 | float[] target = result.array; 86 | for( int z = 0; z < depth; z++ ) { 87 | for( int y = 0; y < height; y++ ) { 88 | for( int x = 0; x < width; x++ ) { 89 | target[index++] = source.getDensity(xBase+x, yBase+y, zBase+z); 90 | } 91 | } 92 | } 93 | return result; 94 | } 95 | 96 | public void extract( DensityVolume source, int xBase, int yBase, int zBase ) { 97 | int index = 0; 98 | for( int z = 0; z < cz; z++ ) { 99 | for( int y = 0; y < cy; y++ ) { 100 | for( int x = 0; x < cx; x++ ) { 101 | array[index++] = source.getDensity(xBase+x, yBase+y, zBase+z); 102 | } 103 | } 104 | } 105 | } 106 | 107 | public void clear() { 108 | Arrays.fill(array, -1); 109 | } 110 | 111 | private int index( int x, int y, int z ) { 112 | /*if( x < 0 || y < 0 || z < 0 || x >= cx || y >= cy || z >= cz ) { 113 | throw new IndexOutOfBoundsException( "(" + x + ", " + y + ", " + z + ") in size:" + cx + ", " + cy + ", " + cz ); 114 | }*/ 115 | return z * cLayer + cx * y + x; 116 | } 117 | 118 | public void setDensity( int x, int y, int z, float d ) { 119 | array[index(x, y, z)] = d; 120 | } 121 | 122 | public float getDensity( int x, int y, int z ) { 123 | return array[index(x, y, z)]; 124 | } 125 | 126 | private double trilinear( float x, float y, float z ) { 127 | int xBase = (int)Math.floor(x); 128 | int yBase = (int)Math.floor(y); 129 | int zBase = (int)Math.floor(z); 130 | int xTop = (int)Math.ceil(x); 131 | int yTop = (int)Math.ceil(y); 132 | int zTop = (int)Math.ceil(z); 133 | 134 | double c000 = array[index(xBase, yBase, zBase)]; 135 | double c100 = array[index(xTop, yBase, zBase)]; 136 | double c101 = array[index(xTop, yBase, zTop)]; 137 | double c001 = array[index(xBase, yBase, zTop)]; 138 | double c010 = array[index(xBase, yTop, zBase)]; 139 | double c110 = array[index(xTop, yTop, zBase)]; 140 | double c111 = array[index(xTop, yTop, zTop)]; 141 | double c011 = array[index(xBase, yTop, zTop)]; 142 | 143 | double xPart = x - xBase; 144 | double cx00 = c000 + (c100 - c000) * xPart; 145 | double cx01 = c001 + (c101 - c001) * xPart; 146 | 147 | double cx10 = c010 + (c110 - c010) * xPart; 148 | double cx11 = c011 + (c111 - c011) * xPart; 149 | 150 | double yPart = y - yBase; 151 | double cxy0 = cx00 + (cx10 - cx00) * yPart; 152 | double cxy1 = cx01 + (cx11 - cx01) * yPart; 153 | 154 | double zPart = z - zBase; 155 | double c = cxy0 + (cxy1 - cxy0) * zPart; 156 | 157 | return c; 158 | } 159 | 160 | 161 | public float getDensity( float x, float y, float z ) { 162 | return (float)trilinear(x, y, z); 163 | } 164 | 165 | public Vector3f getFieldDirection( float x, float y, float z, Vector3f target ) { 166 | 167 | float d = 1f; 168 | 169 | double nx = trilinear(x + d, y, z) 170 | - trilinear(x - d, y, z); 171 | double ny = trilinear(x, y + d, z) 172 | - trilinear(x, y - d, z); 173 | double nz = trilinear(x, y, z + d) 174 | - trilinear(x, y, z - d); 175 | 176 | if( target == null ) { 177 | target = new Vector3f((float)-nx, (float)-ny, (float)-nz).normalizeLocal(); 178 | } else { 179 | target.set((float)-nx, (float)-ny, (float)-nz); 180 | target.normalizeLocal(); 181 | } 182 | 183 | return target; 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/volume/CachingDensityVolume.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.volume; 38 | 39 | import com.google.common.cache.CacheBuilder; 40 | import com.google.common.cache.CacheLoader; 41 | import com.google.common.cache.LoadingCache; 42 | import com.jme3.math.Vector3f; 43 | import com.simsilica.iso.DensityVolume; 44 | import com.simsilica.pager.Grid; 45 | import java.util.concurrent.ExecutionException; 46 | 47 | 48 | /** 49 | * Presents a continuous array-based view of 'chunks' that 50 | * are extracted from a continuous source field. 51 | * 52 | * @author Paul Speed 53 | */ 54 | public class CachingDensityVolume implements DensityVolume { 55 | 56 | private DensityVolume source; 57 | private int xzSize; 58 | private int ySize; 59 | private int superSample; 60 | private Grid grid; 61 | private LoadingCache cache; 62 | 63 | public CachingDensityVolume( int cacheSize, DensityVolume source, int xzSize, int ySize, int xzSuperSample ) { 64 | this.source = source; 65 | this.superSample = xzSuperSample; 66 | this.xzSize = xzSize / xzSuperSample; 67 | this.ySize = ySize; 68 | this.grid = new Grid(xzSize, ySize, xzSize); 69 | 70 | this.cache = CacheBuilder.newBuilder().maximumSize(cacheSize).build(new ChunkLoader()); 71 | } 72 | 73 | public CachingDensityVolume( int cacheSize, DensityVolume source, int xzSize, int ySize ) { 74 | this(cacheSize, source, xzSize, ySize, 1); 75 | } 76 | 77 | private Chunk getChunk( int x, int y, int z ) { 78 | int xCell = grid.toCellX(x); 79 | int yCell = grid.toCellY(y); 80 | int zCell = grid.toCellZ(z); 81 | try { 82 | return cache.get(new ChunkId(xCell, yCell, zCell)); 83 | } catch( ExecutionException ex ) { 84 | throw new RuntimeException("Error creating chunk", ex); 85 | } 86 | } 87 | 88 | private Chunk getChunk( float x, float y, float z ) { 89 | int xCell = grid.toCellX(x); 90 | int yCell = grid.toCellY(y); 91 | int zCell = grid.toCellZ(z); 92 | try { 93 | return cache.get(new ChunkId(xCell, yCell, zCell)); 94 | } catch( ExecutionException ex ) { 95 | throw new RuntimeException("Error creating chunk", ex); 96 | } 97 | } 98 | 99 | @Override 100 | public final float getDensity( int x, int y, int z ) { 101 | Chunk chunk = getChunk(x, y, z); 102 | return chunk.getDensity(x, y, z); 103 | } 104 | 105 | @Override 106 | public final float getDensity( float x, float y, float z ) { 107 | Chunk chunk = getChunk(x, y, z); 108 | return chunk.getDensity(x, y, z); 109 | } 110 | 111 | @Override 112 | public final Vector3f getFieldDirection( float x, float y, float z, Vector3f target ) { 113 | Chunk chunk = getChunk(x, y, z); 114 | return chunk.getFieldDirection(x, y, z, target); 115 | } 116 | 117 | private class ChunkId { 118 | int xChunk; 119 | int yChunk; 120 | int zChunk; 121 | 122 | public ChunkId( int x, int y, int z ) { 123 | this.xChunk = x; 124 | this.yChunk = y; 125 | this.zChunk = z; 126 | } 127 | 128 | @Override 129 | public final int hashCode() { 130 | int hash = 37; 131 | hash += 37 * hash + xChunk; 132 | hash += 37 * hash + yChunk; 133 | hash += 37 * hash + zChunk; 134 | return hash; 135 | } 136 | 137 | @Override 138 | public final boolean equals( Object o ) { 139 | if( o == null || o.getClass() != getClass() ) 140 | return false; 141 | if( o == this ) 142 | return true; 143 | ChunkId other = (ChunkId)o; 144 | if( other.xChunk != xChunk ) 145 | return false; 146 | if( other.yChunk != yChunk ) 147 | return false; 148 | if( other.zChunk != zChunk ) 149 | return false; 150 | return true; 151 | } 152 | 153 | @Override 154 | public String toString() { 155 | return "ChunkId[" + xChunk + ", " + yChunk + ", " + zChunk + "]"; 156 | } 157 | } 158 | 159 | private class Chunk implements DensityVolume { 160 | ChunkId id; 161 | DensityVolume volume; 162 | int xBase; 163 | int yBase; 164 | int zBase; 165 | 166 | public Chunk( ChunkId id, int xBase, int yBase, int zBase, DensityVolume volume ) { 167 | this.id = id; 168 | this.volume = volume; 169 | this.xBase = xBase; 170 | this.yBase = yBase; 171 | this.zBase = zBase; 172 | } 173 | 174 | @Override 175 | public final float getDensity( int x, int y, int z ) { 176 | return volume.getDensity(x - xBase, y - yBase, z - zBase); 177 | } 178 | 179 | @Override 180 | public final float getDensity( float x, float y, float z ) { 181 | return volume.getDensity(x - xBase, y - yBase, z - zBase); 182 | } 183 | 184 | @Override 185 | public final Vector3f getFieldDirection( float x, float y, float z, Vector3f target ) { 186 | return volume.getFieldDirection(x - xBase, y - yBase, z - zBase, target); 187 | } 188 | } 189 | 190 | public static void main( String... args ) { 191 | // Testing something 192 | 193 | DensityVolume source = new DensityVolume() { 194 | @Override 195 | public float getDensity( int x, int y, int z ) { 196 | return x; 197 | } 198 | 199 | @Override 200 | public float getDensity( float x, float y, float z ) { 201 | return x; 202 | } 203 | 204 | @Override 205 | public Vector3f getFieldDirection( float x, float y, float z, Vector3f target ) { 206 | return null; 207 | } 208 | }; 209 | 210 | int xBase = 100; 211 | int superSample = 2; 212 | int xzSize = 16; 213 | int ySize = 32; 214 | 215 | ResamplingVolume resampled = new ResamplingVolume(new Vector3f(superSample, 1, superSample), 216 | new Vector3f(xBase, 0, 0), 217 | source); 218 | 219 | // Since we've moved prescaled origin then our xBase, yBase, etc. are already 220 | // moved for us 221 | DensityVolume result = ArrayDensityVolume.extractVolume(resampled, -2, -2, -2, 222 | xzSize + 4, ySize + 4, xzSize + 4); 223 | 224 | for( int x = 0; x < 16; x++ ) { 225 | System.out.println( "intermediate:" + result.getDensity(x, 0, 0) ); 226 | } 227 | 228 | // Now unscale it... we don't have to unmove it because 229 | // the chunk access is already translating it as if it were an array 230 | // based at 0, 0... but we'll offset for the border we created. 231 | result = new ResamplingVolume(new Vector3f(1f/superSample, 1, 1f/superSample), 232 | new Vector3f(2, 2, 2), 233 | result); 234 | 235 | for( int x = 100; x < 132; x++ ) { 236 | System.out.println( "Original:" + source.getDensity(x, 0, 0) 237 | + " Resampled:" + result.getDensity(x-xBase, 0, 0) ); 238 | } 239 | } 240 | 241 | private class ChunkLoader extends CacheLoader { 242 | 243 | @Override 244 | public Chunk load( ChunkId id ) throws Exception { 245 | 246 | System.out.println( "Loading chunk:" + id ); 247 | int xBase = (int)grid.toWorldX(id.xChunk); 248 | int yBase = (int)grid.toWorldY(id.yChunk); 249 | int zBase = (int)grid.toWorldZ(id.zChunk); 250 | 251 | DensityVolume result; 252 | if( superSample != 1 ) { 253 | // If we are super-sampling then we extract less data but 254 | // treat it like more data. It also means we are extracting 255 | // from a different place, though. 256 | // For example, if we have a cell size of 16 x 16 but we 257 | // are super-sampling *2 then we are really building an array of 258 | // 8 x 8 by skipping every other value. But that means when 259 | // x = 3 in the 'regular world' that it's really 1.5 in the 260 | // super-sampled world. 261 | // Now, we can unscale it when returning and now we can skip 262 | // resolution. 263 | // xzSize has already had supersampling factored in. 264 | ResamplingVolume resampled = new ResamplingVolume(new Vector3f(superSample, 1, superSample), 265 | new Vector3f(xBase, yBase, zBase), 266 | source); 267 | // So we've setup sampling with xBase, yBase, zBase as 0,0,0 in our 268 | // source, and scaled by superSample otherwise 269 | 270 | 271 | // Since we've moved prescaled origin then our xBase, yBase, etc. are already 272 | // moved for us 273 | result = ArrayDensityVolume.extractVolume(resampled, -2, -2, -2, 274 | xzSize + 4, ySize + 4, xzSize + 4); 275 | // The array now represents: 276 | // xBase - 2 * superSample to that plus (xzSize + 4) * superSample 277 | 278 | // Now unscale it... we don't have to unmove it because 279 | // the chunk access is already translating it as if it were an array 280 | // based at 0, 0... but we'll offset for the border we created. 281 | result = new ResamplingVolume(new Vector3f(1f/superSample, 1, 1f/superSample), 282 | new Vector3f(2, 2, 2), 283 | result); 284 | // So, the above indexes 0 as 2, 2, 2 in the array... which represented 285 | // a skipped version of our regular world so we un-sample to index into 286 | // it properly. 287 | } else { 288 | // Make the chunk 2 elements bigger all the way 289 | // around so that the field direction is likely to work 290 | // ArrayDensityVolume samples coordinate + 1... which means 291 | // that 8.5 + 1 = 9.5 which means that we'd need element 10 292 | // also... not just element 9 293 | xBase-=2; 294 | yBase-=2; 295 | zBase-=2; 296 | 297 | result = ArrayDensityVolume.extractVolume(source, xBase, yBase, zBase, 298 | xzSize + 4, ySize + 4, xzSize + 4); 299 | } 300 | 301 | return new Chunk(id, xBase, yBase, zBase, result); 302 | } 303 | } 304 | } 305 | 306 | 307 | -------------------------------------------------------------------------------- /src/main/java/com/simsilica/iso/volume/ResamplingVolume.java: -------------------------------------------------------------------------------- 1 | /* 2 | * $Id$ 3 | * 4 | * Copyright (c) 2014, Simsilica, LLC 5 | * All rights reserved. 6 | * 7 | * Redistribution and use in source and binary forms, with or without 8 | * modification, are permitted provided that the following conditions 9 | * are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in 16 | * the documentation and/or other materials provided with the 17 | * distribution. 18 | * 19 | * 3. Neither the name of the copyright holder nor the names of its 20 | * contributors may be used to endorse or promote products derived 21 | * from this software without specific prior written permission. 22 | * 23 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 24 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 25 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 26 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 27 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 32 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 34 | * OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | 37 | package com.simsilica.iso.volume; 38 | 39 | import com.jme3.math.Vector3f; 40 | import com.simsilica.iso.DensityVolume; 41 | 42 | 43 | /** 44 | * Presents a resampled view of a delegate volume where the 45 | * x, y, and z space may be rescaled. 46 | * 47 | * @author Paul Speed 48 | */ 49 | public class ResamplingVolume implements DensityVolume { 50 | 51 | private Vector3f offset; 52 | private Vector3f scale; 53 | private DensityVolume delegate; 54 | 55 | public ResamplingVolume( Vector3f scale, DensityVolume delegate ) { 56 | this(scale, Vector3f.ZERO, delegate); 57 | } 58 | 59 | public ResamplingVolume( Vector3f scale, Vector3f preScaleOffset, DensityVolume delegate ) { 60 | this.scale = scale.clone(); 61 | this.offset = preScaleOffset.clone(); 62 | this.delegate = delegate; 63 | } 64 | 65 | public float getDensity( int x, int y, int z ) { 66 | return getDensity((float)x, (float)y, (float)z); 67 | } 68 | 69 | public float getDensity( float x, float y, float z ) { 70 | x *= scale.x; 71 | y *= scale.y; 72 | z *= scale.z; 73 | x += offset.x; 74 | y += offset.y; 75 | z += offset.z; 76 | return delegate.getDensity(x, y, z); 77 | } 78 | 79 | public Vector3f getFieldDirection( float x, float y, float z, Vector3f target ) { 80 | float xt = x * scale.x; 81 | float yt = y * scale.y; 82 | float zt = z * scale.z; 83 | xt += offset.x; 84 | yt += offset.y; 85 | zt += offset.z; 86 | Vector3f dir = delegate.getFieldDirection(xt, yt, zt, target); 87 | return dir; 88 | } 89 | 90 | @Override 91 | public String toString() { 92 | return "ResamplingVolume[" + scale + ", " + offset + ", " + delegate + "]"; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/license.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Simsilica, LLC 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in 14 | * the documentation and/or other materials provided with the 15 | * distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived 19 | * from this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 26 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 27 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 28 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 30 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 32 | * OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | --------------------------------------------------------------------------------