diff --git a/Example Materials/Textures/Bricks.meta b/Example Materials/Textures/Bricks.meta
new file mode 100644
index 00000000..df1f9584
--- /dev/null
+++ b/Example Materials/Textures/Bricks.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6b50206ecf728c74dbaec812022d1628
+folderAsset: yes
+timeCreated: 1528532032
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/scsg_bricks_0_d.tga b/Example Materials/Textures/Bricks/scsg_bricks_0_d.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_d.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_0_d.tga
diff --git a/Example Materials/Textures/scsg_bricks_0_d.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_0_d.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_d.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_0_d.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_0_d2.tga b/Example Materials/Textures/Bricks/scsg_bricks_0_d2.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_d2.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_0_d2.tga
diff --git a/Example Materials/Textures/scsg_bricks_0_d2.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_0_d2.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_d2.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_0_d2.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_0_m.tga b/Example Materials/Textures/Bricks/scsg_bricks_0_m.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_m.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_0_m.tga
diff --git a/Example Materials/Textures/scsg_bricks_0_m.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_0_m.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_m.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_0_m.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_0_n.tga b/Example Materials/Textures/Bricks/scsg_bricks_0_n.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_n.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_0_n.tga
diff --git a/Example Materials/Textures/scsg_bricks_0_n.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_0_n.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_n.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_0_n.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_0_o.tga b/Example Materials/Textures/Bricks/scsg_bricks_0_o.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_o.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_0_o.tga
diff --git a/Example Materials/Textures/scsg_bricks_0_o.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_0_o.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_0_o.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_0_o.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_1_d.tga b/Example Materials/Textures/Bricks/scsg_bricks_1_d.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_d.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_1_d.tga
diff --git a/Example Materials/Textures/scsg_bricks_1_d.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_1_d.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_d.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_1_d.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_1_m.tga b/Example Materials/Textures/Bricks/scsg_bricks_1_m.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_m.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_1_m.tga
diff --git a/Example Materials/Textures/scsg_bricks_1_m.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_1_m.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_m.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_1_m.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_1_n.tga b/Example Materials/Textures/Bricks/scsg_bricks_1_n.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_n.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_1_n.tga
diff --git a/Example Materials/Textures/scsg_bricks_1_n.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_1_n.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_n.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_1_n.tga.meta
diff --git a/Example Materials/Textures/scsg_bricks_1_o.tga b/Example Materials/Textures/Bricks/scsg_bricks_1_o.tga
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_o.tga
rename to Example Materials/Textures/Bricks/scsg_bricks_1_o.tga
diff --git a/Example Materials/Textures/scsg_bricks_1_o.tga.meta b/Example Materials/Textures/Bricks/scsg_bricks_1_o.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_bricks_1_o.tga.meta
rename to Example Materials/Textures/Bricks/scsg_bricks_1_o.tga.meta
diff --git a/Example Materials/Textures/Concrete.meta b/Example Materials/Textures/Concrete.meta
new file mode 100644
index 00000000..7afe9c07
--- /dev/null
+++ b/Example Materials/Textures/Concrete.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: c16028ae5d783e84f96784ffbc85b015
+folderAsset: yes
+timeCreated: 1528544735
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Concrete/scsg_concbare_0_d.tga b/Example Materials/Textures/Concrete/scsg_concbare_0_d.tga
new file mode 100644
index 00000000..b8585176
Binary files /dev/null and b/Example Materials/Textures/Concrete/scsg_concbare_0_d.tga differ
diff --git a/Example Materials/Textures/Concrete/scsg_concbare_0_d.tga.meta b/Example Materials/Textures/Concrete/scsg_concbare_0_d.tga.meta
new file mode 100644
index 00000000..97d960d0
--- /dev/null
+++ b/Example Materials/Textures/Concrete/scsg_concbare_0_d.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: cdf6c03eb728a144f936548a854efafd
+timeCreated: 1528544736
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Concrete/scsg_concbare_0_n.tga b/Example Materials/Textures/Concrete/scsg_concbare_0_n.tga
new file mode 100644
index 00000000..1d2204d3
Binary files /dev/null and b/Example Materials/Textures/Concrete/scsg_concbare_0_n.tga differ
diff --git a/Example Materials/Textures/scsg_bricks_0_n2.tga.meta b/Example Materials/Textures/Concrete/scsg_concbare_0_n.tga.meta
similarity index 94%
rename from Example Materials/Textures/scsg_bricks_0_n2.tga.meta
rename to Example Materials/Textures/Concrete/scsg_concbare_0_n.tga.meta
index 4bac71d4..a1581e21 100644
--- a/Example Materials/Textures/scsg_bricks_0_n2.tga.meta
+++ b/Example Materials/Textures/Concrete/scsg_concbare_0_n.tga.meta
@@ -1,6 +1,6 @@
fileFormatVersion: 2
-guid: 69a672569f3684c46aedfaadba2bdca2
-timeCreated: 1522286841
+guid: 966cb224bc730514292e4cff278a71e7
+timeCreated: 1528544856
licenseType: Free
TextureImporter:
fileIDToRecycleName: {}
diff --git a/Example Materials/Textures/Concrete/scsg_concbare_0_s.tga b/Example Materials/Textures/Concrete/scsg_concbare_0_s.tga
new file mode 100644
index 00000000..31d8bd85
Binary files /dev/null and b/Example Materials/Textures/Concrete/scsg_concbare_0_s.tga differ
diff --git a/Example Materials/Textures/Concrete/scsg_concbare_0_s.tga.meta b/Example Materials/Textures/Concrete/scsg_concbare_0_s.tga.meta
new file mode 100644
index 00000000..a5d27f28
--- /dev/null
+++ b/Example Materials/Textures/Concrete/scsg_concbare_0_s.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: ec29e4e9e567f37468184f7a0de020f0
+timeCreated: 1528544824
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Glass.meta b/Example Materials/Textures/Glass.meta
new file mode 100644
index 00000000..7b4494e3
--- /dev/null
+++ b/Example Materials/Textures/Glass.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6a6f960fada39484dbf519a05e3c46c3
+folderAsset: yes
+timeCreated: 1528533788
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Glass/scsg_glass_0_d.tga b/Example Materials/Textures/Glass/scsg_glass_0_d.tga
new file mode 100644
index 00000000..dc337d92
Binary files /dev/null and b/Example Materials/Textures/Glass/scsg_glass_0_d.tga differ
diff --git a/Example Materials/Textures/Glass/scsg_glass_0_d.tga.meta b/Example Materials/Textures/Glass/scsg_glass_0_d.tga.meta
new file mode 100644
index 00000000..363ec352
--- /dev/null
+++ b/Example Materials/Textures/Glass/scsg_glass_0_d.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 29f8b090f8c4ac6459eb4a9ce99d9b44
+timeCreated: 1528533899
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Glass/scsg_glass_0_s.tga b/Example Materials/Textures/Glass/scsg_glass_0_s.tga
new file mode 100644
index 00000000..319f2c17
Binary files /dev/null and b/Example Materials/Textures/Glass/scsg_glass_0_s.tga differ
diff --git a/Example Materials/Textures/Glass/scsg_glass_0_s.tga.meta b/Example Materials/Textures/Glass/scsg_glass_0_s.tga.meta
new file mode 100644
index 00000000..2b479fbb
--- /dev/null
+++ b/Example Materials/Textures/Glass/scsg_glass_0_s.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: cd08c1119455708418517e7f7e065630
+timeCreated: 1528533899
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Glass/scsg_glass_1_d.tga b/Example Materials/Textures/Glass/scsg_glass_1_d.tga
new file mode 100644
index 00000000..d49c4d99
Binary files /dev/null and b/Example Materials/Textures/Glass/scsg_glass_1_d.tga differ
diff --git a/Example Materials/Textures/Glass/scsg_glass_1_d.tga.meta b/Example Materials/Textures/Glass/scsg_glass_1_d.tga.meta
new file mode 100644
index 00000000..15890b67
--- /dev/null
+++ b/Example Materials/Textures/Glass/scsg_glass_1_d.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: e85acc9a9c925fa498710bf9ed20683e
+timeCreated: 1528534760
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Glass/scsg_glass_1_m.tga b/Example Materials/Textures/Glass/scsg_glass_1_m.tga
new file mode 100644
index 00000000..bdd85bb8
Binary files /dev/null and b/Example Materials/Textures/Glass/scsg_glass_1_m.tga differ
diff --git a/Example Materials/Textures/Glass/scsg_glass_1_m.tga.meta b/Example Materials/Textures/Glass/scsg_glass_1_m.tga.meta
new file mode 100644
index 00000000..f54a4140
--- /dev/null
+++ b/Example Materials/Textures/Glass/scsg_glass_1_m.tga.meta
@@ -0,0 +1,84 @@
+fileFormatVersion: 2
+guid: b55438b976068304081119493cf8fe5a
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 5
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 0
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ singleChannelComponent: 0
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - serializedVersion: 2
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Grass.meta b/Example Materials/Textures/Grass.meta
new file mode 100644
index 00000000..41faa194
--- /dev/null
+++ b/Example Materials/Textures/Grass.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 45b359a045a7ca94dab10b34cd1fea64
+folderAsset: yes
+timeCreated: 1528540288
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_d.tga b/Example Materials/Textures/Grass/scsg_grass_0_d.tga
new file mode 100644
index 00000000..32bc4db8
Binary files /dev/null and b/Example Materials/Textures/Grass/scsg_grass_0_d.tga differ
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_d.tga.meta b/Example Materials/Textures/Grass/scsg_grass_0_d.tga.meta
new file mode 100644
index 00000000..8566691e
--- /dev/null
+++ b/Example Materials/Textures/Grass/scsg_grass_0_d.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: a2cdff9107042914daa20038932291de
+timeCreated: 1528540292
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_h.tga b/Example Materials/Textures/Grass/scsg_grass_0_h.tga
new file mode 100644
index 00000000..963b6039
Binary files /dev/null and b/Example Materials/Textures/Grass/scsg_grass_0_h.tga differ
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_h.tga.meta b/Example Materials/Textures/Grass/scsg_grass_0_h.tga.meta
new file mode 100644
index 00000000..a1549783
--- /dev/null
+++ b/Example Materials/Textures/Grass/scsg_grass_0_h.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 12d3c3655bacf2546aa854bed180790b
+timeCreated: 1528540614
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_n.tga b/Example Materials/Textures/Grass/scsg_grass_0_n.tga
new file mode 100644
index 00000000..15852916
Binary files /dev/null and b/Example Materials/Textures/Grass/scsg_grass_0_n.tga differ
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_n.tga.meta b/Example Materials/Textures/Grass/scsg_grass_0_n.tga.meta
new file mode 100644
index 00000000..f748d74f
--- /dev/null
+++ b/Example Materials/Textures/Grass/scsg_grass_0_n.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 4db4329ff2573c9498c5c9c2a7cd52b3
+timeCreated: 1528540467
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 1
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_s.tga b/Example Materials/Textures/Grass/scsg_grass_0_s.tga
new file mode 100644
index 00000000..1e6e2b12
Binary files /dev/null and b/Example Materials/Textures/Grass/scsg_grass_0_s.tga differ
diff --git a/Example Materials/Textures/Grass/scsg_grass_0_s.tga.meta b/Example Materials/Textures/Grass/scsg_grass_0_s.tga.meta
new file mode 100644
index 00000000..1a2a054f
--- /dev/null
+++ b/Example Materials/Textures/Grass/scsg_grass_0_s.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 2f9c7369a93d8194aa364134324bd319
+timeCreated: 1528540289
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal.meta b/Example Materials/Textures/Metal.meta
new file mode 100644
index 00000000..9f2e9d98
--- /dev/null
+++ b/Example Materials/Textures/Metal.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6c1e10dba378b3e41bb81a5323d260a2
+folderAsset: yes
+timeCreated: 1528541868
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal/scsg_mebare_0_d.tga b/Example Materials/Textures/Metal/scsg_mebare_0_d.tga
new file mode 100644
index 00000000..162d32d4
Binary files /dev/null and b/Example Materials/Textures/Metal/scsg_mebare_0_d.tga differ
diff --git a/Example Materials/Textures/Metal/scsg_mebare_0_d.tga.meta b/Example Materials/Textures/Metal/scsg_mebare_0_d.tga.meta
new file mode 100644
index 00000000..4c679078
--- /dev/null
+++ b/Example Materials/Textures/Metal/scsg_mebare_0_d.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: c79bf0cafb75e9645b3e343465f86995
+timeCreated: 1528545425
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal/scsg_mebare_0_m.tga b/Example Materials/Textures/Metal/scsg_mebare_0_m.tga
new file mode 100644
index 00000000..d7054164
Binary files /dev/null and b/Example Materials/Textures/Metal/scsg_mebare_0_m.tga differ
diff --git a/Example Materials/Textures/Metal/scsg_mebare_0_m.tga.meta b/Example Materials/Textures/Metal/scsg_mebare_0_m.tga.meta
new file mode 100644
index 00000000..2eca234b
--- /dev/null
+++ b/Example Materials/Textures/Metal/scsg_mebare_0_m.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: d21b2a6fc12e1fa4d95e540af87306cb
+timeCreated: 1528545426
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal/scsg_mebare_0_n.tga b/Example Materials/Textures/Metal/scsg_mebare_0_n.tga
new file mode 100644
index 00000000..9757f8d7
Binary files /dev/null and b/Example Materials/Textures/Metal/scsg_mebare_0_n.tga differ
diff --git a/Example Materials/Textures/Metal/scsg_mebare_0_n.tga.meta b/Example Materials/Textures/Metal/scsg_mebare_0_n.tga.meta
new file mode 100644
index 00000000..d5ac9914
--- /dev/null
+++ b/Example Materials/Textures/Metal/scsg_mebare_0_n.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: f5eb4653ba02586428fade72ccc6713e
+timeCreated: 1528545479
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 1
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_d.tga b/Example Materials/Textures/Metal/scsg_metreadplate_0_d.tga
new file mode 100644
index 00000000..b8fa33dc
Binary files /dev/null and b/Example Materials/Textures/Metal/scsg_metreadplate_0_d.tga differ
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_d.tga.meta b/Example Materials/Textures/Metal/scsg_metreadplate_0_d.tga.meta
new file mode 100644
index 00000000..c063173a
--- /dev/null
+++ b/Example Materials/Textures/Metal/scsg_metreadplate_0_d.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 438f56b13bd907d4b9deaafdba0a1c0e
+timeCreated: 1528541869
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_h.tga b/Example Materials/Textures/Metal/scsg_metreadplate_0_h.tga
new file mode 100644
index 00000000..45085b55
Binary files /dev/null and b/Example Materials/Textures/Metal/scsg_metreadplate_0_h.tga differ
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_h.tga.meta b/Example Materials/Textures/Metal/scsg_metreadplate_0_h.tga.meta
new file mode 100644
index 00000000..f6e1a9ce
--- /dev/null
+++ b/Example Materials/Textures/Metal/scsg_metreadplate_0_h.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: e56821a3d00728d4f9c6372f2b758056
+timeCreated: 1528541914
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_m.tga b/Example Materials/Textures/Metal/scsg_metreadplate_0_m.tga
new file mode 100644
index 00000000..36fa30b5
Binary files /dev/null and b/Example Materials/Textures/Metal/scsg_metreadplate_0_m.tga differ
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_m.tga.meta b/Example Materials/Textures/Metal/scsg_metreadplate_0_m.tga.meta
new file mode 100644
index 00000000..66920115
--- /dev/null
+++ b/Example Materials/Textures/Metal/scsg_metreadplate_0_m.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 2caaba55fcf7fa949a99c6b73ac2ecb4
+timeCreated: 1528541868
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_n.tga b/Example Materials/Textures/Metal/scsg_metreadplate_0_n.tga
new file mode 100644
index 00000000..117716c3
Binary files /dev/null and b/Example Materials/Textures/Metal/scsg_metreadplate_0_n.tga differ
diff --git a/Example Materials/Textures/Metal/scsg_metreadplate_0_n.tga.meta b/Example Materials/Textures/Metal/scsg_metreadplate_0_n.tga.meta
new file mode 100644
index 00000000..e4bb30ff
--- /dev/null
+++ b/Example Materials/Textures/Metal/scsg_metreadplate_0_n.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 93a83a4887fd51f4e9c90090f2e5aa49
+timeCreated: 1528543687
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 1
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Planks.meta b/Example Materials/Textures/Planks.meta
new file mode 100644
index 00000000..cb1a8659
--- /dev/null
+++ b/Example Materials/Textures/Planks.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 35179d642acb9a3468e49ec75fe561c6
+folderAsset: yes
+timeCreated: 1528532051
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/scsg_planks_0_d.tga b/Example Materials/Textures/Planks/scsg_planks_0_d.tga
similarity index 100%
rename from Example Materials/Textures/scsg_planks_0_d.tga
rename to Example Materials/Textures/Planks/scsg_planks_0_d.tga
diff --git a/Example Materials/Textures/scsg_planks_0_d.tga.meta b/Example Materials/Textures/Planks/scsg_planks_0_d.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_planks_0_d.tga.meta
rename to Example Materials/Textures/Planks/scsg_planks_0_d.tga.meta
diff --git a/Example Materials/Textures/scsg_planks_0_m.tga b/Example Materials/Textures/Planks/scsg_planks_0_m.tga
similarity index 100%
rename from Example Materials/Textures/scsg_planks_0_m.tga
rename to Example Materials/Textures/Planks/scsg_planks_0_m.tga
diff --git a/Example Materials/Textures/scsg_planks_0_m.tga.meta b/Example Materials/Textures/Planks/scsg_planks_0_m.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_planks_0_m.tga.meta
rename to Example Materials/Textures/Planks/scsg_planks_0_m.tga.meta
diff --git a/Example Materials/Textures/scsg_planks_0_n.tga b/Example Materials/Textures/Planks/scsg_planks_0_n.tga
similarity index 100%
rename from Example Materials/Textures/scsg_planks_0_n.tga
rename to Example Materials/Textures/Planks/scsg_planks_0_n.tga
diff --git a/Example Materials/Textures/scsg_planks_0_n.tga.meta b/Example Materials/Textures/Planks/scsg_planks_0_n.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_planks_0_n.tga.meta
rename to Example Materials/Textures/Planks/scsg_planks_0_n.tga.meta
diff --git a/Example Materials/Textures/scsg_planks_1_d.tga b/Example Materials/Textures/Planks/scsg_planks_1_d.tga
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_d.tga
rename to Example Materials/Textures/Planks/scsg_planks_1_d.tga
diff --git a/Example Materials/Textures/scsg_planks_1_d.tga.meta b/Example Materials/Textures/Planks/scsg_planks_1_d.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_d.tga.meta
rename to Example Materials/Textures/Planks/scsg_planks_1_d.tga.meta
diff --git a/Example Materials/Textures/scsg_planks_1_m.tga b/Example Materials/Textures/Planks/scsg_planks_1_m.tga
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_m.tga
rename to Example Materials/Textures/Planks/scsg_planks_1_m.tga
diff --git a/Example Materials/Textures/scsg_planks_1_m.tga.meta b/Example Materials/Textures/Planks/scsg_planks_1_m.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_m.tga.meta
rename to Example Materials/Textures/Planks/scsg_planks_1_m.tga.meta
diff --git a/Example Materials/Textures/scsg_planks_1_n.tga b/Example Materials/Textures/Planks/scsg_planks_1_n.tga
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_n.tga
rename to Example Materials/Textures/Planks/scsg_planks_1_n.tga
diff --git a/Example Materials/Textures/scsg_planks_1_n.tga.meta b/Example Materials/Textures/Planks/scsg_planks_1_n.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_n.tga.meta
rename to Example Materials/Textures/Planks/scsg_planks_1_n.tga.meta
diff --git a/Example Materials/Textures/scsg_planks_1_o.tga b/Example Materials/Textures/Planks/scsg_planks_1_o.tga
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_o.tga
rename to Example Materials/Textures/Planks/scsg_planks_1_o.tga
diff --git a/Example Materials/Textures/scsg_planks_1_o.tga.meta b/Example Materials/Textures/Planks/scsg_planks_1_o.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_planks_1_o.tga.meta
rename to Example Materials/Textures/Planks/scsg_planks_1_o.tga.meta
diff --git a/Example Materials/Textures/Soil.meta b/Example Materials/Textures/Soil.meta
new file mode 100644
index 00000000..9aee62b4
--- /dev/null
+++ b/Example Materials/Textures/Soil.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8f951b6f1704eda4cadab0347b9f4ba6
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_d.tga b/Example Materials/Textures/Soil/scsg_soilsand_0_d.tga
new file mode 100644
index 00000000..c89618b4
Binary files /dev/null and b/Example Materials/Textures/Soil/scsg_soilsand_0_d.tga differ
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_d.tga.meta b/Example Materials/Textures/Soil/scsg_soilsand_0_d.tga.meta
new file mode 100644
index 00000000..6a147b98
--- /dev/null
+++ b/Example Materials/Textures/Soil/scsg_soilsand_0_d.tga.meta
@@ -0,0 +1,55 @@
+fileFormatVersion: 2
+guid: 59054ab926b8ed64cb87f9645cc39583
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -5
+ maxTextureSize: 1024
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 100
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 0
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_h.tga b/Example Materials/Textures/Soil/scsg_soilsand_0_h.tga
new file mode 100644
index 00000000..27142712
Binary files /dev/null and b/Example Materials/Textures/Soil/scsg_soilsand_0_h.tga differ
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_h.tga.meta b/Example Materials/Textures/Soil/scsg_soilsand_0_h.tga.meta
new file mode 100644
index 00000000..7cbb3550
--- /dev/null
+++ b/Example Materials/Textures/Soil/scsg_soilsand_0_h.tga.meta
@@ -0,0 +1,55 @@
+fileFormatVersion: 2
+guid: bf58e192f8ec9e14795f5a1c8911a589
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -5
+ maxTextureSize: 1024
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 100
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 0
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_m.tga b/Example Materials/Textures/Soil/scsg_soilsand_0_m.tga
new file mode 100644
index 00000000..c30f836f
Binary files /dev/null and b/Example Materials/Textures/Soil/scsg_soilsand_0_m.tga differ
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_m.tga.meta b/Example Materials/Textures/Soil/scsg_soilsand_0_m.tga.meta
new file mode 100644
index 00000000..b63a2f6e
--- /dev/null
+++ b/Example Materials/Textures/Soil/scsg_soilsand_0_m.tga.meta
@@ -0,0 +1,55 @@
+fileFormatVersion: 2
+guid: 87d79ce3806c2aa4fb80f0c8acc4f983
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -5
+ maxTextureSize: 1024
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 100
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 0
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_n.tga b/Example Materials/Textures/Soil/scsg_soilsand_0_n.tga
new file mode 100644
index 00000000..511f1b1e
Binary files /dev/null and b/Example Materials/Textures/Soil/scsg_soilsand_0_n.tga differ
diff --git a/Example Materials/Textures/Soil/scsg_soilsand_0_n.tga.meta b/Example Materials/Textures/Soil/scsg_soilsand_0_n.tga.meta
new file mode 100644
index 00000000..eddcc792
--- /dev/null
+++ b/Example Materials/Textures/Soil/scsg_soilsand_0_n.tga.meta
@@ -0,0 +1,55 @@
+fileFormatVersion: 2
+guid: 84a9d8b366812cf4eb85b30b63ed46cb
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 1
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -5
+ maxTextureSize: 1024
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 100
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Utility.meta b/Example Materials/Textures/Utility.meta
new file mode 100644
index 00000000..367870b7
--- /dev/null
+++ b/Example Materials/Textures/Utility.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: f32ed04f416dc0849bc96756ab5ec4b4
+folderAsset: yes
+timeCreated: 1528532067
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/scsg_default_map_blue.tga b/Example Materials/Textures/Utility/scsg_default_map_blue.tga
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_blue.tga
rename to Example Materials/Textures/Utility/scsg_default_map_blue.tga
diff --git a/Example Materials/Textures/scsg_default_map_blue.tga.meta b/Example Materials/Textures/Utility/scsg_default_map_blue.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_blue.tga.meta
rename to Example Materials/Textures/Utility/scsg_default_map_blue.tga.meta
diff --git a/Example Materials/Textures/scsg_default_map_cyan.tga b/Example Materials/Textures/Utility/scsg_default_map_cyan.tga
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_cyan.tga
rename to Example Materials/Textures/Utility/scsg_default_map_cyan.tga
diff --git a/Example Materials/Textures/scsg_default_map_cyan.tga.meta b/Example Materials/Textures/Utility/scsg_default_map_cyan.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_cyan.tga.meta
rename to Example Materials/Textures/Utility/scsg_default_map_cyan.tga.meta
diff --git a/Example Materials/Textures/scsg_default_map_grey.tga b/Example Materials/Textures/Utility/scsg_default_map_grey.tga
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_grey.tga
rename to Example Materials/Textures/Utility/scsg_default_map_grey.tga
diff --git a/Example Materials/Textures/scsg_default_map_grey.tga.meta b/Example Materials/Textures/Utility/scsg_default_map_grey.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_grey.tga.meta
rename to Example Materials/Textures/Utility/scsg_default_map_grey.tga.meta
diff --git a/Example Materials/Textures/scsg_default_map_purple.tga b/Example Materials/Textures/Utility/scsg_default_map_purple.tga
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_purple.tga
rename to Example Materials/Textures/Utility/scsg_default_map_purple.tga
diff --git a/Example Materials/Textures/scsg_default_map_purple.tga.meta b/Example Materials/Textures/Utility/scsg_default_map_purple.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_purple.tga.meta
rename to Example Materials/Textures/Utility/scsg_default_map_purple.tga.meta
diff --git a/Example Materials/Textures/scsg_default_map_red.tga b/Example Materials/Textures/Utility/scsg_default_map_red.tga
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_red.tga
rename to Example Materials/Textures/Utility/scsg_default_map_red.tga
diff --git a/Example Materials/Textures/scsg_default_map_red.tga.meta b/Example Materials/Textures/Utility/scsg_default_map_red.tga.meta
similarity index 100%
rename from Example Materials/Textures/scsg_default_map_red.tga.meta
rename to Example Materials/Textures/Utility/scsg_default_map_red.tga.meta
diff --git a/Example Materials/Textures/Wood.meta b/Example Materials/Textures/Wood.meta
new file mode 100644
index 00000000..89c47268
--- /dev/null
+++ b/Example Materials/Textures/Wood.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 959e3d6dff60b364a8ae086c551145c5
+folderAsset: yes
+timeCreated: 1528539157
+licenseType: Free
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Wood/scsg_osb_0_d.tga b/Example Materials/Textures/Wood/scsg_osb_0_d.tga
new file mode 100644
index 00000000..800da319
Binary files /dev/null and b/Example Materials/Textures/Wood/scsg_osb_0_d.tga differ
diff --git a/Example Materials/Textures/Wood/scsg_osb_0_d.tga.meta b/Example Materials/Textures/Wood/scsg_osb_0_d.tga.meta
new file mode 100644
index 00000000..43efc291
--- /dev/null
+++ b/Example Materials/Textures/Wood/scsg_osb_0_d.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 0ddee2c4875aa3540b1d83293f34cb39
+timeCreated: 1528539158
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Wood/scsg_osb_0_n.tga b/Example Materials/Textures/Wood/scsg_osb_0_n.tga
new file mode 100644
index 00000000..5eaeb10b
Binary files /dev/null and b/Example Materials/Textures/Wood/scsg_osb_0_n.tga differ
diff --git a/Example Materials/Textures/Wood/scsg_osb_0_n.tga.meta b/Example Materials/Textures/Wood/scsg_osb_0_n.tga.meta
new file mode 100644
index 00000000..4804717f
--- /dev/null
+++ b/Example Materials/Textures/Wood/scsg_osb_0_n.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 5f2c6d47b635dec448403742b2f274d5
+timeCreated: 1528539202
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 1
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: 1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/Wood/scsg_osb_0_s.tga b/Example Materials/Textures/Wood/scsg_osb_0_s.tga
new file mode 100644
index 00000000..0b0cbe06
Binary files /dev/null and b/Example Materials/Textures/Wood/scsg_osb_0_s.tga differ
diff --git a/Example Materials/Textures/Wood/scsg_osb_0_s.tga.meta b/Example Materials/Textures/Wood/scsg_osb_0_s.tga.meta
new file mode 100644
index 00000000..20f74a36
--- /dev/null
+++ b/Example Materials/Textures/Wood/scsg_osb_0_s.tga.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 463ec8e21934d094ab5631d356602bec
+timeCreated: 1528539158
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/Textures/scsg_bricks_0_n2.tga b/Example Materials/Textures/scsg_bricks_0_n2.tga
deleted file mode 100644
index e6b807a6..00000000
Binary files a/Example Materials/Textures/scsg_bricks_0_n2.tga and /dev/null differ
diff --git a/Example Materials/Textures/update.txt b/Example Materials/Textures/update.txt
index e4b31e58..fd4e08cc 100644
--- a/Example Materials/Textures/update.txt
+++ b/Example Materials/Textures/update.txt
@@ -1,11 +1,25 @@
Content updated by Daniel Cornelius (Kerfuffles)
-Wood textures provided by Textures.com (formerly CGTextures.com)
+Wood, soil, and grass diffuse textures provided by Textures.com (formerly CGTextures.com)
-[3.28.2018]
+[06.09.2018]
++ added materials and their content:
+ scsg_soilsand_0
+ scsg_grass_0
+ scsg_glass_0
+ scsg_glass_1
+ scsg_concbare_0
+ scsg_mebare_0
+ scsg_metreadplate_0
+ scsg_osb_0
+
+* modified scsg_bricks_0 to have better looking grout
+- removed scsg_bricks_0_n2.tga
+
+[03.28.2018]
* fixed wrong project version - was using the wrong version of unity and had .meta issues with older versions.
* added credit for wood textures to the top of update.txt
-[3.27.2018]
+[03.27.2018]
* updated all materials
* updated all textures
* updated default materials to use mobile diffuse shaders
diff --git a/Example Materials/scsg_bricks_0.mat b/Example Materials/scsg_bricks_0.mat
index 46d76b2f..58e021de 100644
Binary files a/Example Materials/scsg_bricks_0.mat and b/Example Materials/scsg_bricks_0.mat differ
diff --git a/Example Materials/scsg_concbare_0.mat b/Example Materials/scsg_concbare_0.mat
new file mode 100644
index 00000000..0e2f1cf9
Binary files /dev/null and b/Example Materials/scsg_concbare_0.mat differ
diff --git a/Example Materials/scsg_concbare_0.mat.meta b/Example Materials/scsg_concbare_0.mat.meta
new file mode 100644
index 00000000..32b37336
--- /dev/null
+++ b/Example Materials/scsg_concbare_0.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3913a9987bf129f449b9aea2364584ae
+timeCreated: 1528544846
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/scsg_glass_0.mat b/Example Materials/scsg_glass_0.mat
new file mode 100644
index 00000000..5110e3c8
Binary files /dev/null and b/Example Materials/scsg_glass_0.mat differ
diff --git a/Example Materials/scsg_glass_0.mat.meta b/Example Materials/scsg_glass_0.mat.meta
new file mode 100644
index 00000000..023c33ec
--- /dev/null
+++ b/Example Materials/scsg_glass_0.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 44117e50618dda44695c9d0b519fce98
+timeCreated: 1528533797
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/scsg_glass_1.mat b/Example Materials/scsg_glass_1.mat
new file mode 100644
index 00000000..a5d75bdd
Binary files /dev/null and b/Example Materials/scsg_glass_1.mat differ
diff --git a/Example Materials/scsg_glass_1.mat.meta b/Example Materials/scsg_glass_1.mat.meta
new file mode 100644
index 00000000..98374715
--- /dev/null
+++ b/Example Materials/scsg_glass_1.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dd48af89bad145f42a5435464299f373
+timeCreated: 1528534280
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/scsg_grass_0.mat b/Example Materials/scsg_grass_0.mat
new file mode 100644
index 00000000..e0b572ea
Binary files /dev/null and b/Example Materials/scsg_grass_0.mat differ
diff --git a/Example Materials/scsg_grass_0.mat.meta b/Example Materials/scsg_grass_0.mat.meta
new file mode 100644
index 00000000..2ff5c758
--- /dev/null
+++ b/Example Materials/scsg_grass_0.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5f48d61d06f6395469c9a51431b9b760
+timeCreated: 1528540313
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/scsg_mebare_0.mat b/Example Materials/scsg_mebare_0.mat
new file mode 100644
index 00000000..2765a393
Binary files /dev/null and b/Example Materials/scsg_mebare_0.mat differ
diff --git a/Example Materials/scsg_mebare_0.mat.meta b/Example Materials/scsg_mebare_0.mat.meta
new file mode 100644
index 00000000..a61f7383
--- /dev/null
+++ b/Example Materials/scsg_mebare_0.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a817f471166507340b24ab3333f70a58
+timeCreated: 1528545459
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/scsg_metreadplate_0.mat b/Example Materials/scsg_metreadplate_0.mat
new file mode 100644
index 00000000..a4333d01
Binary files /dev/null and b/Example Materials/scsg_metreadplate_0.mat differ
diff --git a/Example Materials/scsg_metreadplate_0.mat.meta b/Example Materials/scsg_metreadplate_0.mat.meta
new file mode 100644
index 00000000..0f0e56a0
--- /dev/null
+++ b/Example Materials/scsg_metreadplate_0.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0a30577de9e884c44b5d755b5388d471
+timeCreated: 1528541879
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/scsg_osb_0.mat b/Example Materials/scsg_osb_0.mat
new file mode 100644
index 00000000..9398fed1
Binary files /dev/null and b/Example Materials/scsg_osb_0.mat differ
diff --git a/Example Materials/scsg_osb_0.mat.meta b/Example Materials/scsg_osb_0.mat.meta
new file mode 100644
index 00000000..d3f9dea0
--- /dev/null
+++ b/Example Materials/scsg_osb_0.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: fb0ace6ce9bc68440b188c0545c53b1f
+timeCreated: 1528539176
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Example Materials/scsg_soilsand_0.mat b/Example Materials/scsg_soilsand_0.mat
new file mode 100644
index 00000000..79e39cf2
Binary files /dev/null and b/Example Materials/scsg_soilsand_0.mat differ
diff --git a/Example Materials/scsg_soilsand_0.mat.meta b/Example Materials/scsg_soilsand_0.mat.meta
new file mode 100644
index 00000000..f87dc259
--- /dev/null
+++ b/Example Materials/scsg_soilsand_0.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b161333b4708c8c43b340407eedd0773
+timeCreated: 1528531337
+licenseType: Free
+NativeFormatImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Gizmos/ButtonCapsule.png b/Gizmos/ButtonCapsule.png
new file mode 100644
index 00000000..ac0441b1
Binary files /dev/null and b/Gizmos/ButtonCapsule.png differ
diff --git a/Gizmos/ButtonCapsule.png.meta b/Gizmos/ButtonCapsule.png.meta
new file mode 100644
index 00000000..28251b86
--- /dev/null
+++ b/Gizmos/ButtonCapsule.png.meta
@@ -0,0 +1,77 @@
+fileFormatVersion: 2
+guid: 1a8135251802e5b4a939a5d911e9a716
+timeCreated: 1526153801
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaUsage: 1
+ alphaIsTransparency: 0
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Gizmos/Collision.png b/Gizmos/Collision.png
new file mode 100644
index 00000000..02db0fa1
Binary files /dev/null and b/Gizmos/Collision.png differ
diff --git a/Gizmos/Collision.png.meta b/Gizmos/Collision.png.meta
new file mode 100644
index 00000000..3740abe7
--- /dev/null
+++ b/Gizmos/Collision.png.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: 38ae470ed5d6a5946b50522f0178823b
+timeCreated: 1527521195
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -3
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: 0
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Gizmos/Group.png.meta b/Gizmos/Group.png.meta
index 24fcda58..e0661334 100644
--- a/Gizmos/Group.png.meta
+++ b/Gizmos/Group.png.meta
@@ -4,17 +4,14 @@ timeCreated: 1521296720
licenseType: Free
TextureImporter:
fileIDToRecycleName: {}
- externalObjects: {}
- serializedVersion: 4
+ serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
- sRGBTexture: 0
- linearTexture: 0
+ linearTexture: 1
+ correctGamma: 0
fadeOut: 0
borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
@@ -24,22 +21,23 @@ TextureImporter:
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
- generateCubemap: 6
+ generateCubemap: 0
cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
- textureFormat: 1
+ textureFormat: -3
maxTextureSize: 2048
textureSettings:
- serializedVersion: 2
filterMode: 0
aniso: 1
mipBias: -1
- wrapU: 1
- wrapV: 1
- wrapW: -1
+ wrapMode: 1
nPOTScale: 0
lightmap: 0
+ rGBM: 0
compressionQuality: 50
+ allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
@@ -47,40 +45,12 @@ TextureImporter:
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
- alphaUsage: 1
alphaIsTransparency: 1
- spriteTessellationDetail: -1
textureType: 2
- textureShape: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- platformSettings:
- - buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
- - buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
+ buildTargetSettings: []
spriteSheet:
- serializedVersion: 2
sprites: []
outline: []
- physicsShape: []
spritePackingTag:
userData:
assetBundleName:
diff --git a/Gizmos/ImporterBackground.png.meta b/Gizmos/ImporterBackground.png.meta
index ff384295..737c3f21 100644
--- a/Gizmos/ImporterBackground.png.meta
+++ b/Gizmos/ImporterBackground.png.meta
@@ -4,17 +4,14 @@ timeCreated: 1526753113
licenseType: Free
TextureImporter:
fileIDToRecycleName: {}
- externalObjects: {}
- serializedVersion: 4
+ serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
- sRGBTexture: 0
- linearTexture: 0
+ linearTexture: 1
+ correctGamma: 0
fadeOut: 0
borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
@@ -24,22 +21,23 @@ TextureImporter:
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
- generateCubemap: 6
+ generateCubemap: 0
cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
- textureFormat: 1
+ textureFormat: -3
maxTextureSize: 2048
textureSettings:
- serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -1
- wrapU: 1
- wrapV: 1
- wrapW: -1
+ wrapMode: 1
nPOTScale: 0
lightmap: 0
+ rGBM: 0
compressionQuality: 50
+ allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
@@ -47,40 +45,12 @@ TextureImporter:
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
- alphaUsage: 1
alphaIsTransparency: 1
- spriteTessellationDetail: -1
textureType: 2
- textureShape: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- platformSettings:
- - buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
- - buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
+ buildTargetSettings: []
spriteSheet:
- serializedVersion: 2
sprites: []
outline: []
- physicsShape: []
spritePackingTag:
userData:
assetBundleName:
diff --git a/Gizmos/ImporterUnrealGold.png.meta b/Gizmos/ImporterUnrealGold.png.meta
index f0c6be2c..12aeba92 100644
--- a/Gizmos/ImporterUnrealGold.png.meta
+++ b/Gizmos/ImporterUnrealGold.png.meta
@@ -4,17 +4,14 @@ timeCreated: 1526752452
licenseType: Free
TextureImporter:
fileIDToRecycleName: {}
- externalObjects: {}
- serializedVersion: 4
+ serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
- sRGBTexture: 0
- linearTexture: 0
+ linearTexture: 1
+ correctGamma: 0
fadeOut: 0
borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
@@ -24,22 +21,23 @@ TextureImporter:
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
- generateCubemap: 6
+ generateCubemap: 0
cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
- textureFormat: 1
+ textureFormat: -3
maxTextureSize: 2048
textureSettings:
- serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -1
- wrapU: 1
- wrapV: 1
- wrapW: -1
+ wrapMode: 1
nPOTScale: 0
lightmap: 0
+ rGBM: 0
compressionQuality: 50
+ allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
@@ -47,40 +45,12 @@ TextureImporter:
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
- alphaUsage: 1
alphaIsTransparency: 1
- spriteTessellationDetail: -1
textureType: 2
- textureShape: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- platformSettings:
- - buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
- - buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
+ buildTargetSettings: []
spriteSheet:
- serializedVersion: 2
sprites: []
outline: []
- physicsShape: []
spritePackingTag:
userData:
assetBundleName:
diff --git a/Gizmos/ImporterValveMapFormat2006.png.meta b/Gizmos/ImporterValveMapFormat2006.png.meta
index 5815773b..955f2bf2 100644
--- a/Gizmos/ImporterValveMapFormat2006.png.meta
+++ b/Gizmos/ImporterValveMapFormat2006.png.meta
@@ -4,17 +4,14 @@ timeCreated: 1526754071
licenseType: Free
TextureImporter:
fileIDToRecycleName: {}
- externalObjects: {}
- serializedVersion: 4
+ serializedVersion: 2
mipmaps:
mipMapMode: 0
enableMipMap: 0
- sRGBTexture: 0
- linearTexture: 0
+ linearTexture: 1
+ correctGamma: 0
fadeOut: 0
borderMipMap: 0
- mipMapsPreserveCoverage: 0
- alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
@@ -24,22 +21,23 @@ TextureImporter:
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
- generateCubemap: 6
+ generateCubemap: 0
cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
seamlessCubemap: 0
- textureFormat: 1
+ textureFormat: -3
maxTextureSize: 2048
textureSettings:
- serializedVersion: 2
filterMode: -1
aniso: 1
mipBias: -1
- wrapU: 1
- wrapV: 1
- wrapW: -1
+ wrapMode: 1
nPOTScale: 0
lightmap: 0
+ rGBM: 0
compressionQuality: 50
+ allowsAlphaSplitting: 0
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
@@ -47,40 +45,12 @@ TextureImporter:
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
- alphaUsage: 1
alphaIsTransparency: 1
- spriteTessellationDetail: -1
textureType: 2
- textureShape: 1
- maxTextureSizeSet: 0
- compressionQualitySet: 0
- textureFormatSet: 0
- platformSettings:
- - buildTarget: DefaultTexturePlatform
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
- - buildTarget: Standalone
- maxTextureSize: 2048
- resizeAlgorithm: 0
- textureFormat: -1
- textureCompression: 0
- compressionQuality: 50
- crunchedCompression: 0
- allowsAlphaSplitting: 0
- overridden: 0
- androidETC2FallbackOverride: 0
+ buildTargetSettings: []
spriteSheet:
- serializedVersion: 2
sprites: []
outline: []
- physicsShape: []
spritePackingTag:
userData:
assetBundleName:
diff --git a/Gizmos/ShapeEditorRestore.png b/Gizmos/ShapeEditorRestore.png
new file mode 100644
index 00000000..54d1e88e
Binary files /dev/null and b/Gizmos/ShapeEditorRestore.png differ
diff --git a/Gizmos/ShapeEditorRestore.png.meta b/Gizmos/ShapeEditorRestore.png.meta
new file mode 100644
index 00000000..82fad4b4
--- /dev/null
+++ b/Gizmos/ShapeEditorRestore.png.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: f5dd9f530c3638e408070593f1d32483
+timeCreated: 1529322008
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -3
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Gizmos/ShapeEditorSegmentExtrude.png b/Gizmos/ShapeEditorSegmentExtrude.png
new file mode 100644
index 00000000..5a3a9b5a
Binary files /dev/null and b/Gizmos/ShapeEditorSegmentExtrude.png differ
diff --git a/Gizmos/ShapeEditorSegmentExtrude.png.meta b/Gizmos/ShapeEditorSegmentExtrude.png.meta
new file mode 100644
index 00000000..d26f5d29
--- /dev/null
+++ b/Gizmos/ShapeEditorSegmentExtrude.png.meta
@@ -0,0 +1,87 @@
+fileFormatVersion: 2
+guid: 7f1df97a46e257f4c8b767500efb54ad
+timeCreated: 1528144163
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 0
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: -1
+ aniso: 1
+ mipBias: -1
+ wrapU: 1
+ wrapV: 1
+ wrapW: -1
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 2
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Gizmos/Volume.png b/Gizmos/Volume.png
new file mode 100644
index 00000000..58cfe3f3
Binary files /dev/null and b/Gizmos/Volume.png differ
diff --git a/Gizmos/Volume.png.meta b/Gizmos/Volume.png.meta
new file mode 100644
index 00000000..bc8e89b7
--- /dev/null
+++ b/Gizmos/Volume.png.meta
@@ -0,0 +1,57 @@
+fileFormatVersion: 2
+guid: d7669e97eab7cc4418122503e0afc3f2
+timeCreated: 1528581972
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -3
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: 0
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Materials/Collision.mat b/Materials/Collision.mat
new file mode 100644
index 00000000..c16170f2
Binary files /dev/null and b/Materials/Collision.mat differ
diff --git a/Materials/Collision.mat.meta b/Materials/Collision.mat.meta
new file mode 100644
index 00000000..36f9c98f
--- /dev/null
+++ b/Materials/Collision.mat.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: c521ea577cb0b82468daae0d21107c48
+timeCreated: 1527500345
+licenseType: Free
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Materials/NoCSG.mat b/Materials/NoCSG.mat
index f0aa4ee6..2bd94c9c 100644
Binary files a/Materials/NoCSG.mat and b/Materials/NoCSG.mat differ
diff --git a/Materials/Subtract.mat b/Materials/Subtract.mat
index e113f7a5..0a38f681 100644
Binary files a/Materials/Subtract.mat and b/Materials/Subtract.mat differ
diff --git a/Materials/Volume.mat b/Materials/Volume.mat
new file mode 100644
index 00000000..444695bb
Binary files /dev/null and b/Materials/Volume.mat differ
diff --git a/Materials/Volume.mat.meta b/Materials/Volume.mat.meta
new file mode 100644
index 00000000..035eebcc
--- /dev/null
+++ b/Materials/Volume.mat.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 7722e52ff56119a48abbc9f90fb9ad3f
+timeCreated: 1528582390
+licenseType: Free
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Materials/scsg_volume_physics.mat b/Materials/scsg_volume_physics.mat
new file mode 100644
index 00000000..f0fb56a4
Binary files /dev/null and b/Materials/scsg_volume_physics.mat differ
diff --git a/Materials/scsg_volume_physics.mat.meta b/Materials/scsg_volume_physics.mat.meta
new file mode 100644
index 00000000..cabf6ddc
--- /dev/null
+++ b/Materials/scsg_volume_physics.mat.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: c2a2861cf86a2a94cb01e41ebff5c57a
+timeCreated: 1528799488
+licenseType: Free
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Materials/scsg_volume_physics.png b/Materials/scsg_volume_physics.png
new file mode 100644
index 00000000..838a9fc9
Binary files /dev/null and b/Materials/scsg_volume_physics.png differ
diff --git a/Materials/scsg_volume_physics.png.meta b/Materials/scsg_volume_physics.png.meta
new file mode 100644
index 00000000..598cbca7
--- /dev/null
+++ b/Materials/scsg_volume_physics.png.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 3d4db9ee3a5a5f44b977479eef6d8381
+timeCreated: 1528799478
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Materials/scsg_volume_trigger.mat b/Materials/scsg_volume_trigger.mat
new file mode 100644
index 00000000..98e0169a
Binary files /dev/null and b/Materials/scsg_volume_trigger.mat differ
diff --git a/Materials/scsg_volume_trigger.mat.meta b/Materials/scsg_volume_trigger.mat.meta
new file mode 100644
index 00000000..237eca22
--- /dev/null
+++ b/Materials/scsg_volume_trigger.mat.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: d2a09e04545951545bea1edea9e49ce8
+timeCreated: 1528798992
+licenseType: Free
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 2100000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Materials/scsg_volume_trigger.png b/Materials/scsg_volume_trigger.png
new file mode 100644
index 00000000..7996a80e
Binary files /dev/null and b/Materials/scsg_volume_trigger.png differ
diff --git a/Materials/scsg_volume_trigger.png.meta b/Materials/scsg_volume_trigger.png.meta
new file mode 100644
index 00000000..c041c237
--- /dev/null
+++ b/Materials/scsg_volume_trigger.png.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 62c4c3521da6bec4c9550995c2ba861b
+timeCreated: 1528799267
+licenseType: Free
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/README.md b/README.md
index c4f7a675..bd303f8e 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@ Using Constructive Solid Geometry techniques SabreCSG allows you to add and subt
SabreCSG was originally sold on the [Unity Asset Store](https://www.assetstore.unity3d.com/en/#!/content/47418), but as of 14th October 2016 has been [open sourced](http://sabrecsg.com/2016/10/14/sabrecsg-released-as-open-source/) under the MIT License (see LICENSE for details). It is now maintained on this [Github repository](https://github.com/sabresaurus/SabreCSG).
-Core Features:
+## Core Features:
- Boolean CSG algorithm supporting partial rebuilding.
- Draw tools to rapidly create levels by drawing brushes directly.
@@ -25,3 +25,21 @@ Core Features:
- Experimental Code API - Experimental support for procedurally generating levels through code.
Documentation and videos on how to use SabreCSG are available at the [SabreCSG Learn website](http://sabrecsg.com/learn/) or the [wiki](https://github.com/sabresaurus/SabreCSG/wiki).
+
+## Download
+
+[![Download SabreCSG](https://github.com/sabresaurus/SabreCSG/wiki/images/home/home-button-1.png)](https://github.com/sabresaurus/SabreCSG/archive/master.zip)
+
+[![Installation Instructions](https://github.com/sabresaurus/SabreCSG/wiki/images/home/home-button-4.png)](https://youtu.be/KDheSZcagsw?t=30)
+
+## Documentation
+
+[![Visit Wiki Pages](https://github.com/sabresaurus/SabreCSG/wiki/images/home/home-button-3.png)](https://github.com/sabresaurus/SabreCSG/wiki)
+
+[![Join the Official SabreCSG Discord Server](https://github.com/sabresaurus/SabreCSG/wiki/images/home/home-button-2.png)](https://discord.gg/pFAS5rK)
+
+## Donations:
+
+If you love SabreCSG and wish to say thanks then please feel free to make a donation. Your donations are a huge motivator to continue the development of SabreCSG.
+
+[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://paypal.me/henrydejongh)
diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs
new file mode 100644
index 00000000..f28e346a
--- /dev/null
+++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG
+{
+ [CanEditMultipleObjects]
+ [CustomEditor(typeof(HollowBoxBrush), true)]
+ public class HollowBoxBrushInspector : CompoundBrushInspector
+ {
+ SerializedProperty wallThicknessProp;
+
+ public override void DoInspectorGUI()
+ {
+ using (new NamedVerticalScope("Hollow Box"))
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.PropertyField(wallThicknessProp);
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ ApplyAndInvalidate();
+ }
+
+ EditorGUILayout.Space();
+ }
+
+ base.DoInspectorGUI();
+ }
+
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+
+ // Setup the SerializedProperties.
+ wallThicknessProp = serializedObject.FindProperty("wallThickness");
+ }
+
+ private void ApplyAndInvalidate()
+ {
+ serializedObject.ApplyModifiedProperties();
+ System.Array.ForEach(BrushTargets, item => item.Invalidate(true));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs.meta b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs.meta
new file mode 100644
index 00000000..28fb2868
--- /dev/null
+++ b/Scripts/Brushes/CompoundBrushes/Editor/HollowBoxBrushInspector.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: 9a76fa0614387084cb85b598c0744270
+timeCreated: 1528366657
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorBrushInspector.cs b/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorBrushInspector.cs
index 7ff6a7db..8b510c46 100644
--- a/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorBrushInspector.cs
+++ b/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorBrushInspector.cs
@@ -18,14 +18,14 @@ public override void DoInspectorGUI()
{
GUILayout.BeginHorizontal(EditorStyles.toolbar);
GUIStyle createBrushStyle = new GUIStyle(EditorStyles.toolbarButton);
- if (GUILayout.Button(new GUIContent(" Show Editor", SabreCSGResources.ButtonShapeEditorTexture, "Show 2D Shape Editor"), createBrushStyle))
+ if (GUILayout.Button(new GUIContent(" Shape Editor", SabreCSGResources.ButtonShapeEditorTexture, "Show 2D Shape Editor"), createBrushStyle))
{
// display the 2d shape ditor.
ShapeEditorWindow.Init();
}
- if (GUILayout.Button(new GUIContent(" Load Project", SabreCSGResources.ShapeEditorOpenTexture, "Load Embedded Project Into 2D Shape Editor"), createBrushStyle))
+ if (GUILayout.Button(new GUIContent(" Restore Project", SabreCSGResources.ShapeEditorRestoreTexture, "Load Embedded Project Into 2D Shape Editor"), createBrushStyle))
{
- if (EditorUtility.DisplayDialog("2D Shape Editor", "Are you sure you wish to load the embedded project?\r\nAny unsaved work in the 2D Shape Editor will be lost!", "Yes", "No"))
+ if (EditorUtility.DisplayDialog("2D Shape Editor", "Are you sure you wish to restore the embedded project?\r\nAny unsaved work in the 2D Shape Editor will be lost!", "Yes", "No"))
{
// display the 2d shape ditor.
ShapeEditorWindow window = ShapeEditorWindow.InitAndGetHandle();
diff --git a/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs b/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs
index 73ea04d8..3a216b3c 100644
--- a/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs
+++ b/Scripts/Brushes/CompoundBrushes/Editor/ShapeEditorWindow.cs
@@ -8,6 +8,7 @@
using System.Reflection;
using UnityEditor;
using UnityEngine;
+using UnityEngine.Rendering;
namespace Sabresaurus.SabreCSG.ShapeEditor
{
@@ -510,6 +511,11 @@ private void OnGUI()
OnSegmentInsert();
Event.current.Use();
}
+ if (Event.current.keyCode == KeyCode.E)
+ {
+ OnSegmentExtrude();
+ Event.current.Use();
+ }
if (Event.current.keyCode == KeyCode.Delete)
{
OnDelete();
@@ -619,14 +625,51 @@ private void OnGUI()
gridMaterial = new Material(shader);
}
+ bool isOpenGL = false;
+
+ if (SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLCore
+#if !UNITY_5_3_OR_NEWER
+ || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGL2
+#endif
+#if UNITY_5_1 || UNITY_5_2 || UNITY_5_3_0 || UNITY_5_3_1 || UNITY_5_3_2 || UNITY_5_3_3 || UNITY_5_3_OR_NEWER
+ || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES2
+ || SystemInfo.graphicsDeviceType == GraphicsDeviceType.OpenGLES3
+#endif
+ )
+ {
+ isOpenGL = true;
+ }
+
+ float pixelsPerPoint = 1;
+#if UNITY_5_4_OR_NEWER
+ pixelsPerPoint = EditorGUIUtility.pixelsPerPoint;
+#endif
+
// draw the grid using the special grid shader:
bool docked = isDocked;
gridMaterial.SetFloat("_OffsetX", GetViewportRect().x + (docked ? 2 : 0)); // why is this neccesary, what's moving?
- gridMaterial.SetFloat("_OffsetY", GetViewportRect().y + (docked ? 0 : 3)); // why is this neccesary, what's moving?
+ if (isOpenGL)
+ {
+ if (pixelsPerPoint > 1)
+ {
+ gridMaterial.SetFloat("_OffsetY", (docked ? 3 : 0)); // why is this neccesary, what's moving?
+ }
+ else
+ {
+ gridMaterial.SetFloat("_OffsetY", GetViewportRect().y + (docked ? 3 : 0)); // why is this neccesary, what's moving?
+ }
+ }
+ else
+ {
+ gridMaterial.SetFloat("_OffsetY", GetViewportRect().y + (docked ? 0 : 3)); // why is this neccesary, what's moving?
+ }
+
gridMaterial.SetFloat("_ScrollX", viewportScroll.x);
gridMaterial.SetFloat("_ScrollY", viewportScroll.y);
gridMaterial.SetFloat("_Zoom", gridScale);
+ gridMaterial.SetFloat("_Height", GetViewportRect().height);
gridMaterial.SetTexture("_Background", backgroundImage);
+ gridMaterial.SetFloat("_PixelsPerPoint", pixelsPerPoint);
gridMaterial.SetPass(0);
GL.Begin(GL.QUADS);
@@ -637,6 +680,8 @@ private void OnGUI()
GL.End();
///////////////////////////////////////////////////////////////////////////////////
+ lineMaterial.SetFloat("_Height", GetViewportRect().height);
+ lineMaterial.SetFloat("_CutoffY", 35.0f);
lineMaterial.SetPass(0);
GL.PushMatrix();
@@ -787,6 +832,10 @@ private void OnGUI()
{
OnSegmentInsert();
}
+ if (GUILayout.Button(new GUIContent(SabreCSGResources.ShapeEditorSegmentExtrudeTexture, "Extrude Segment(s) (E)"), createBrushStyle))
+ {
+ OnSegmentExtrude();
+ }
if (GUILayout.Button(new GUIContent(SabreCSGResources.ShapeEditorDeleteTexture, "Delete Segment(s) or Shape(s) (DEL)"), createBrushStyle))
{
OnDelete();
@@ -1078,7 +1127,7 @@ private void OnZoomOut()
private void OnHome()
{
// scroll to the center of the screen.
-#if UNITY_2017_2_OR_NEWER
+#if UNITY_2017_2_OR_NEWER && !UNITY_2017_2_0
viewportScroll = new Vector2(Screen.safeArea.width / 2.0f / EditorGUIUtility.pixelsPerPoint, Screen.safeArea.height / 2.0f / EditorGUIUtility.pixelsPerPoint);
#else
viewportScroll = new Vector2(Screen.width / 2.0f, Screen.height / 2.0f);
@@ -1140,6 +1189,31 @@ private void OnSegmentInsert()
}
}
+ ///
+ /// Called when the extrude segment button is pressed. Will extrude all selected segments.
+ ///
+ private void OnSegmentExtrude()
+ {
+ foreach (Segment segment in selectedSegments.ToArray()) // use .ToArray() to iterate a clone.
+ {
+ bool inverted = project.flipHorizontally ^ project.flipVertically;
+ Segment next = GetNextSegment(segment);
+ // calculate extrude direction.
+ Vector2Int pos1 = (inverted ? segment.position - next.position : next.position - segment.position);
+ Vector2 dir = new Vector2(pos1.y, -pos1.x).normalized * 2.0f;
+ // insert new segments at an extruded distance.
+ Shape parent = GetShapeOfSegment(segment);
+ Segment select;
+ parent.segments.Insert(parent.segments.IndexOf(next), select = new Segment(segment.position.x + Mathf.RoundToInt(dir.x), segment.position.y + Mathf.RoundToInt(dir.y)));
+ parent.segments.Insert(parent.segments.IndexOf(next), new Segment(next.position.x + Mathf.RoundToInt(dir.x), next.position.y + Mathf.RoundToInt(dir.y)));
+ // recalculate the pivot position of the shape.
+ parent.CalculatePivotPosition();
+ // update the selection so we have the extruded segment selected, this improves the workflow experience.
+ selectedObjects.Remove(segment);
+ selectedObjects.Add(select);
+ }
+ }
+
///
/// Called when the delete button is pressed. Will delete all selected segments and shapes.
///
@@ -1452,7 +1526,7 @@ private void OnToolsPivotResetPosition()
private Rect GetViewportRect()
{
-#if UNITY_2017_2_OR_NEWER
+#if UNITY_2017_2_OR_NEWER && !UNITY_2017_2_0
Rect viewportRect = Screen.safeArea;
#else
Rect viewportRect = new Rect(0, 0, Screen.width, Screen.height);
@@ -1584,7 +1658,7 @@ private void ShowCenteredPopupWindowContent(PopupWindowContent popup)
Vector2 size = popup.GetWindowSize();
try
{
-#if UNITY_2017_2_OR_NEWER
+#if UNITY_2017_2_OR_NEWER && !UNITY_2017_2_0
PopupWindow.Show(new Rect((Screen.safeArea.width / 2.0f / EditorGUIUtility.pixelsPerPoint) - (size.x / 2.0f), (Screen.safeArea.height / 2.0f / EditorGUIUtility.pixelsPerPoint) - (size.y / 2.0f), 0, 0), popup);
#else
PopupWindow.Show(new Rect((Screen.width / 2.0f) - (size.x / 2.0f), (Screen.height / 2.0f) - (size.y / 2.0f), 0, 0), popup);
diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs
new file mode 100644
index 00000000..00fe96aa
--- /dev/null
+++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs
@@ -0,0 +1,119 @@
+#if UNITY_EDITOR || RUNTIME_CSG
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG
+{
+ [ExecuteInEditMode]
+ public class HollowBoxBrush : CompoundBrush
+ {
+ ///
+ /// Gets or sets the thickness of the walls.
+ ///
+ /// The thickness of the walls.
+ public float WallThickness
+ {
+ get
+ {
+ return wallThickness;
+ }
+ set
+ {
+ wallThickness = value;
+ }
+ }
+
+ ///
+ /// Gets the beautiful name of the brush used in auto-generation of the hierarchy name.
+ ///
+ /// The beautiful name of the brush.
+ public override string BeautifulBrushName
+ {
+ get
+ {
+ return "Hollow Box Brush";
+ }
+ }
+
+ public override int BrushCount
+ {
+ get
+ {
+ // If the brush is too small for walls, just default to a single brush.
+ return (IsBrushXYZTooSmall) ? 2 : 1;
+ }
+ }
+
+ ///
+ /// Is the size of the bounds X, Y, and Z above the minimum size?
+ ///
+ ///
+ public bool IsBrushXYZTooSmall
+ {
+ get
+ {
+ return localBounds.size.x > wallThickness * 2.0f && localBounds.size.z > wallThickness * 2.0f && localBounds.size.y > wallThickness * 2;
+ }
+ }
+
+ ///
+ /// The thickness of the walls.
+ ///
+ [SerializeField]
+ private float wallThickness = 0.25f;
+
+ public override void UpdateVisibility()
+ {
+ }
+
+ public override void Invalidate(bool polygonsChanged)
+ {
+ base.Invalidate(polygonsChanged);
+
+ for (int i = 0; i < BrushCount; i++)
+ {
+ generatedBrushes[i].Mode = this.Mode;
+ generatedBrushes[i].IsNoCSG = this.IsNoCSG;
+ generatedBrushes[i].IsVisible = this.IsVisible;
+ generatedBrushes[i].HasCollision = this.HasCollision;
+ }
+
+ if (IsBrushXYZTooSmall)
+ {
+ generatedBrushes[0].Mode = CSGMode.Add;
+ BrushUtility.Resize(generatedBrushes[0], localBounds.size);
+
+ generatedBrushes[1].Mode = CSGMode.Subtract;
+ BrushUtility.Resize(generatedBrushes[1], localBounds.size - new Vector3(wallThickness * 2, wallThickness * 2, wallThickness * 2));
+
+ for (int i = 0; i < BrushCount; i++)
+ {
+ generatedBrushes[i].SetPolygons(GeneratePolys(i));
+ generatedBrushes[i].Invalidate(true);
+ }
+ }
+ else
+ {
+ BrushUtility.Resize(generatedBrushes[0], localBounds.size);
+ }
+ }
+
+ private Polygon[] GeneratePolys(int index)
+ {
+ Polygon[] output = generatedBrushes[index].GetPolygons();
+
+ for (int i = 0; i < 6; i++)
+ {
+ output[i].ResetVertexNormals();
+ output[i].GenerateUvCoordinates();
+ }
+
+ return output;
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs.meta b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs.meta
new file mode 100644
index 00000000..2e6895a0
--- /dev/null
+++ b/Scripts/Brushes/CompoundBrushes/HollowBoxBrush.cs.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: eed65674ef786f142a6e53243f320513
+timeCreated: 1528366664
+licenseType: Free
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/PrimitiveBrush.cs b/Scripts/Brushes/PrimitiveBrush.cs
old mode 100755
new mode 100644
index f42745d7..47976bb5
--- a/Scripts/Brushes/PrimitiveBrush.cs
+++ b/Scripts/Brushes/PrimitiveBrush.cs
@@ -16,6 +16,7 @@ public enum PrimitiveBrushType
Custom,
IcoSphere,
Cone,
+ Capsule
};
///
@@ -63,6 +64,30 @@ public class PrimitiveBrush : Brush
/// The cone side count.
public int ConeSideCount { get { return coneSideCount; } set { coneSideCount = value; } }
+ ///
+ /// The capsule side count.
+ ///
+ [SerializeField, HideInInspector]
+ private int capsuleSideCount = 12;
+
+ ///
+ /// Gets or sets the capsule side count.
+ ///
+ /// The capsule side count.
+ public int CapsuleSideCount { get { return capsuleSideCount; } set { capsuleSideCount = value; } }
+
+ ///
+ /// The capsule height.
+ ///
+ [SerializeField, HideInInspector]
+ private float capsuleHeight = 2.0f;
+
+ ///
+ /// Gets or sets the capsule height.
+ ///
+ /// The capsule height.
+ public float CapsuleHeight { get { return capsuleHeight; } set { capsuleHeight = value; } }
+
///
/// The sphere side count.
///
@@ -172,6 +197,9 @@ public override string BeautifulBrushName
case PrimitiveBrushType.Cone:
return "Cone Brush";
+ case PrimitiveBrushType.Capsule:
+ return "Capsule Brush";
+
default:
return base.BeautifulBrushName;
}
@@ -303,6 +331,18 @@ public void ResetPolygons()
}
polygons = BrushFactory.GenerateCone(coneSideCount);
}
+ else if (brushType == PrimitiveBrushType.Capsule)
+ {
+ if (capsuleHeight < 0.001f)
+ {
+ capsuleHeight = 0.001f;
+ }
+ if (capsuleSideCount < 3)
+ {
+ capsuleSideCount = 3;
+ }
+ polygons = BrushFactory.GenerateCapsule(capsuleHeight, 0.5f, capsuleSideCount);
+ }
else if (brushType == Sabresaurus.SabreCSG.PrimitiveBrushType.Custom)
{
// Do nothing
@@ -711,7 +751,15 @@ public override void Invalidate(bool polygonsChanged)
#if UNITY_EDITOR
Material material;
- if (IsNoCSG)
+ if (this.mode == CSGMode.Volume)
+ {
+ material = Volume ? Volume.BrushPreviewMaterial : SabreCSGResources.GetVolumeMaterial();
+ }
+ else if (!IsVisible)
+ {
+ material = SabreCSGResources.GetCollisionMaterial();
+ }
+ else if (IsNoCSG)
{
material = SabreCSGResources.GetNoCSGMaterial();
}
@@ -762,7 +810,11 @@ public override void UpdateVisibility()
MeshRenderer meshRenderer = GetComponent();
if (meshRenderer != null)
{
- meshRenderer.enabled = isVisible && !CurrentSettings.ShowBrushesAsWireframes;
+#if UNITY_EDITOR
+ meshRenderer.enabled = mode == CSGMode.Volume || (isVisible && !CurrentSettings.ShowBrushesAsWireframes);
+#else
+ meshRenderer.enabled = isVisible;
+#endif
}
}
@@ -944,6 +996,12 @@ public GameObject Duplicate()
// once parented they will end up at world position 0,0,0 if this step isn't done.
newObject.transform.position = this.transform.position;
+ // properly duplicate the volume type.
+ if (Volume != null)
+ {
+ Volume = ScriptableObject.Instantiate(Volume);
+ }
+
return newObject;
}
diff --git a/Scripts/Brushes/Volumes.meta b/Scripts/Brushes/Volumes.meta
new file mode 100644
index 00000000..8e76ac47
--- /dev/null
+++ b/Scripts/Brushes/Volumes.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 60c6ee95e9ddc4148b8b0a0236e4b715
+folderAsset: yes
+timeCreated: 1528584004
+licenseType: Free
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume.meta b/Scripts/Brushes/Volumes/PhysicsVolume.meta
new file mode 100644
index 00000000..4d5950cf
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: a8ff45a5b8964b046bdeac2a9417a2c9
+folderAsset: yes
+timeCreated: 1528723867
+licenseType: Free
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolume.cs b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolume.cs
new file mode 100644
index 00000000..e3f3d875
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolume.cs
@@ -0,0 +1,409 @@
+#if UNITY_EDITOR || RUNTIME_CSG
+
+using System;
+using System.Linq;
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// Applies forces to rigid bodies inside of the volume.
+ ///
+ ///
+ [Serializable]
+ public class PhysicsVolume : Volume
+ {
+ ///
+ /// The force mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceMode forceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The force space mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceSpace forceSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The force applied to rigid bodies.
+ ///
+ [SerializeField]
+ public Vector3 force = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The relative force mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceMode relativeForceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The relative force space mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceSpace relativeForceSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The relative force applied to rigid bodies.
+ ///
+ [SerializeField]
+ public Vector3 relativeForce = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The torque force mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceMode torqueForceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The torque force space mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceSpace torqueSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The torque applied to rigid bodies.
+ ///
+ [SerializeField]
+ public Vector3 torque = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The relative torque force mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceMode relativeTorqueForceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The relative torque force space mode applied to rigid bodies.
+ ///
+ [SerializeField]
+ public PhysicsVolumeForceSpace relativeTorqueSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The relative torque applied to rigid bodies.
+ ///
+ [SerializeField]
+ public Vector3 relativeTorque = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The gravity settings applied to rigid bodies inside the volume.
+ ///
+ [SerializeField]
+ public PhysicsVolumeGravityMode gravity = PhysicsVolumeGravityMode.None;
+
+ ///
+ /// The layer mask to limit the effects of the physics volume to specific layers.
+ ///
+ [SerializeField]
+ public LayerMask layer = -1;
+
+ ///
+ /// Whether to use a filter tag.
+ ///
+ [SerializeField]
+ public bool useFilterTag = false;
+
+ ///
+ /// The filter tag to limit the effects of the physics volume to specific tags.
+ ///
+ [SerializeField]
+ public string filterTag = "Untagged";
+
+#if UNITY_EDITOR
+
+ ///
+ /// Gets the brush preview material shown in the editor.
+ ///
+ public override Material BrushPreviewMaterial
+ {
+ get
+ {
+ return (Material)SabreCSGResources.LoadObject("Materials/scsg_volume_physics.mat");
+ }
+ }
+
+ ///
+ /// Called when the inspector GUI is drawn in the editor.
+ ///
+ /// The selected volumes in the editor (for multi-editing).
+ /// True if a property changed or else false.
+ public override bool OnInspectorGUI(Volume[] selectedVolumes)
+ {
+ var physicsVolumes = selectedVolumes.Cast();
+ bool invalidate = false;
+
+ // global force:
+
+ GUILayout.BeginVertical("Box");
+ {
+ UnityEditor.EditorGUILayout.LabelField("Force Options", UnityEditor.EditorStyles.boldLabel);
+ GUILayout.Space(4);
+
+ UnityEditor.EditorGUI.indentLevel = 1;
+ GUILayout.BeginVertical();
+ {
+ PhysicsVolumeForceMode previousPhysicsVolumeForceMode;
+ forceMode = (PhysicsVolumeForceMode)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Mode", "The force mode."), previousPhysicsVolumeForceMode = forceMode);
+ if (previousPhysicsVolumeForceMode != forceMode)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.forceMode = forceMode;
+ invalidate = true;
+ }
+
+ if (forceMode != PhysicsVolumeForceMode.None)
+ {
+ Vector3 previousVector3;
+ UnityEditor.EditorGUIUtility.wideMode = true;
+ force = UnityEditor.EditorGUILayout.Vector3Field(new GUIContent("Force", "The amount of force."), previousVector3 = force);
+ UnityEditor.EditorGUIUtility.wideMode = false;
+ if (previousVector3 != force)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.force = force;
+ invalidate = true;
+ }
+
+ PhysicsVolumeForceSpace previousPhysicsVolumeForceSpace;
+ forceSpace = (PhysicsVolumeForceSpace)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Space", "The force space mode."), previousPhysicsVolumeForceSpace = forceSpace);
+ if (previousPhysicsVolumeForceSpace != forceSpace)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.forceSpace = forceSpace;
+ invalidate = true;
+ }
+ }
+ }
+ GUILayout.EndVertical();
+ UnityEditor.EditorGUI.indentLevel = 0;
+ }
+ GUILayout.EndVertical();
+
+ // relative force:
+
+ GUILayout.BeginVertical("Box");
+ {
+ UnityEditor.EditorGUILayout.LabelField("Relative Force Options", UnityEditor.EditorStyles.boldLabel);
+ GUILayout.Space(4);
+
+ UnityEditor.EditorGUI.indentLevel = 1;
+ GUILayout.BeginVertical();
+ {
+ PhysicsVolumeForceMode previousPhysicsVolumeRelativeForceMode;
+ relativeForceMode = (PhysicsVolumeForceMode)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Mode", "The relative force mode."), previousPhysicsVolumeRelativeForceMode = relativeForceMode);
+ if (previousPhysicsVolumeRelativeForceMode != relativeForceMode)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.relativeForceMode = relativeForceMode;
+ invalidate = true;
+ }
+
+ if (relativeForceMode != PhysicsVolumeForceMode.None)
+ {
+ Vector3 previousVector3;
+ UnityEditor.EditorGUIUtility.wideMode = true;
+ relativeForce = UnityEditor.EditorGUILayout.Vector3Field(new GUIContent("Force", "The amount of relative force."), previousVector3 = relativeForce);
+ UnityEditor.EditorGUIUtility.wideMode = false;
+ if (previousVector3 != relativeForce)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.relativeForce = relativeForce;
+ invalidate = true;
+ }
+
+ PhysicsVolumeForceSpace previousPhysicsVolumeForceSpace;
+ relativeForceSpace = (PhysicsVolumeForceSpace)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Space", "The relative force space mode."), previousPhysicsVolumeForceSpace = relativeForceSpace);
+ if (previousPhysicsVolumeForceSpace != relativeForceSpace)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.relativeForceSpace = relativeForceSpace;
+ invalidate = true;
+ }
+ }
+ }
+ GUILayout.EndVertical();
+ UnityEditor.EditorGUI.indentLevel = 0;
+ }
+ GUILayout.EndVertical();
+
+ // global torque:
+
+ GUILayout.BeginVertical("Box");
+ {
+ UnityEditor.EditorGUILayout.LabelField("Torque Options", UnityEditor.EditorStyles.boldLabel);
+ GUILayout.Space(4);
+
+ UnityEditor.EditorGUI.indentLevel = 1;
+ GUILayout.BeginVertical();
+ {
+ PhysicsVolumeForceMode previousPhysicsVolumeTorqueForceMode;
+ torqueForceMode = (PhysicsVolumeForceMode)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Mode", "The torque force mode."), previousPhysicsVolumeTorqueForceMode = torqueForceMode);
+ if (previousPhysicsVolumeTorqueForceMode != torqueForceMode)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.torqueForceMode = torqueForceMode;
+ invalidate = true;
+ }
+
+ if (torqueForceMode != PhysicsVolumeForceMode.None)
+ {
+ Vector3 previousVector3;
+ UnityEditor.EditorGUIUtility.wideMode = true;
+ torque = UnityEditor.EditorGUILayout.Vector3Field(new GUIContent("Force", "The amount of torque force."), previousVector3 = torque);
+ UnityEditor.EditorGUIUtility.wideMode = false;
+ if (previousVector3 != torque)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.torque = torque;
+ invalidate = true;
+ }
+
+ PhysicsVolumeForceSpace previousPhysicsVolumeForceSpace;
+ torqueSpace = (PhysicsVolumeForceSpace)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Space", "The torque force space mode."), previousPhysicsVolumeForceSpace = torqueSpace);
+ if (previousPhysicsVolumeForceSpace != torqueSpace)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.torqueSpace = torqueSpace;
+ invalidate = true;
+ }
+ }
+ }
+ GUILayout.EndVertical();
+ UnityEditor.EditorGUI.indentLevel = 0;
+ }
+ GUILayout.EndVertical();
+
+ // relative torque:
+
+ GUILayout.BeginVertical("Box");
+ {
+ UnityEditor.EditorGUILayout.LabelField("Relative Torque Options", UnityEditor.EditorStyles.boldLabel);
+ GUILayout.Space(4);
+
+ UnityEditor.EditorGUI.indentLevel = 1;
+ GUILayout.BeginVertical();
+ {
+ PhysicsVolumeForceMode previousPhysicsVolumeRelativeTorqueForceMode;
+ relativeTorqueForceMode = (PhysicsVolumeForceMode)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Mode", "The relative torque force mode."), previousPhysicsVolumeRelativeTorqueForceMode = relativeTorqueForceMode);
+ if (previousPhysicsVolumeRelativeTorqueForceMode != relativeTorqueForceMode)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.relativeTorqueForceMode = relativeTorqueForceMode;
+ invalidate = true;
+ }
+
+ if (relativeTorqueForceMode != PhysicsVolumeForceMode.None)
+ {
+ Vector3 previousVector3;
+ UnityEditor.EditorGUIUtility.wideMode = true;
+ relativeTorque = UnityEditor.EditorGUILayout.Vector3Field(new GUIContent("Force", "The amount of relative torque force."), previousVector3 = relativeTorque);
+ UnityEditor.EditorGUIUtility.wideMode = false;
+ if (previousVector3 != relativeTorque)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.relativeTorque = relativeTorque;
+ invalidate = true;
+ }
+
+ PhysicsVolumeForceSpace previousPhysicsVolumeForceSpace;
+ relativeTorqueSpace = (PhysicsVolumeForceSpace)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Force Space", "The relative torque force space mode."), previousPhysicsVolumeForceSpace = relativeTorqueSpace);
+ if (previousPhysicsVolumeForceSpace != relativeTorqueSpace)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.relativeTorqueSpace = relativeTorqueSpace;
+ invalidate = true;
+ }
+ }
+ }
+ GUILayout.EndVertical();
+ UnityEditor.EditorGUI.indentLevel = 0;
+ }
+ GUILayout.EndVertical();
+
+ // general options:
+
+ GUILayout.BeginVertical("Box");
+ {
+ UnityEditor.EditorGUILayout.LabelField("General Options", UnityEditor.EditorStyles.boldLabel);
+ GUILayout.Space(4);
+
+ UnityEditor.EditorGUI.indentLevel = 1;
+ GUILayout.BeginVertical();
+ {
+ LayerMask previousLayerMask;
+ layer = SabreGUILayout.LayerMaskField(new GUIContent("Layer Mask", "The layer mask to limit the effects of the physics volume to specific layers."), (previousLayerMask = layer).value);
+ if (previousLayerMask != layer)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.layer = layer;
+ invalidate = true;
+ }
+
+ bool previousBoolean;
+ useFilterTag = UnityEditor.EditorGUILayout.Toggle(new GUIContent("Use Filter Tag", "Whether to use a filter tag."), previousBoolean = useFilterTag);
+ if (useFilterTag != previousBoolean)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.useFilterTag = useFilterTag;
+ invalidate = true;
+ }
+
+ if (useFilterTag)
+ {
+ string previousString;
+ filterTag = UnityEditor.EditorGUILayout.TagField(new GUIContent("Filter Tag", "The filter tag to limit the effects of the physics volume to specific tags."), previousString = filterTag);
+ if (filterTag != previousString)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.filterTag = filterTag;
+ invalidate = true;
+ }
+ }
+
+ PhysicsVolumeGravityMode previousPhysicsVolumeGravityMode;
+ gravity = (PhysicsVolumeGravityMode)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Gravity", "The gravity settings applied to rigid bodies inside the volume."), previousPhysicsVolumeGravityMode = gravity);
+ if (previousPhysicsVolumeGravityMode != gravity)
+ {
+ foreach (PhysicsVolume volume in physicsVolumes)
+ volume.gravity = gravity;
+ invalidate = true;
+ }
+ }
+ GUILayout.EndVertical();
+ UnityEditor.EditorGUI.indentLevel = 0;
+ }
+ GUILayout.EndVertical();
+
+ return invalidate; // true when a property changed, the brush invalidates and stores all changes.
+ }
+
+#endif
+
+ ///
+ /// Called when the volume is created in the editor.
+ ///
+ /// The generated volume game object.
+ public override void OnCreateVolume(GameObject volume)
+ {
+ PhysicsVolumeComponent component = volume.AddComponent();
+ component.forceMode = forceMode;
+ component.forceSpace = forceSpace;
+ component.force = force;
+ component.relativeForceMode = relativeForceMode;
+ component.relativeForceSpace = relativeForceSpace;
+ component.relativeForce = relativeForce;
+ component.torqueForceMode = torqueForceMode;
+ component.torqueSpace = torqueSpace;
+ component.torque = torque;
+ component.relativeTorqueForceMode = relativeTorqueForceMode;
+ component.relativeTorqueSpace = relativeTorqueSpace;
+ component.relativeTorque = relativeTorque;
+ component.gravity = gravity;
+ component.layer = layer;
+ component.useFilterTag = useFilterTag;
+ component.filterTag = filterTag;
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolume.cs.meta b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolume.cs.meta
new file mode 100644
index 00000000..45e0761f
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolume.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: e14cbf429a81a8540a53f69c3803d2a4
+timeCreated: 1528723867
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeComponent.cs b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeComponent.cs
new file mode 100644
index 00000000..ce2dcb14
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeComponent.cs
@@ -0,0 +1,288 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// Applies forces to rigid bodies inside of the volume.
+ ///
+ ///
+ public class PhysicsVolumeComponent : MonoBehaviour
+ {
+ ///
+ /// Represents a rigidbody that is currently inside of the volume.
+ ///
+ private class TrackedRigidbody
+ {
+ ///
+ /// The rigidbody inside of the volume.
+ ///
+ public Rigidbody rigidbody;
+
+ ///
+ /// Whether the rigidbody had gravity before entering the volume.
+ ///
+ public bool hadGravity;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The rigidbody inside of the volume.
+ public TrackedRigidbody(Rigidbody rigidbody)
+ {
+ // track information about the rigidbody.
+ this.rigidbody = rigidbody;
+ this.hadGravity = rigidbody.useGravity;
+ }
+ }
+
+ ///
+ /// The force mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceMode forceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The force space mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceSpace forceSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The force applied to rigid bodies.
+ ///
+ public Vector3 force = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The relative force mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceMode relativeForceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The relative force space mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceSpace relativeForceSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The relative force applied to rigid bodies.
+ ///
+ public Vector3 relativeForce = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The torque force mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceMode torqueForceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The torque force space mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceSpace torqueSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The torque applied to rigid bodies.
+ ///
+ public Vector3 torque = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The relative torque force mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceMode relativeTorqueForceMode = PhysicsVolumeForceMode.None;
+
+ ///
+ /// The relative torque force space mode applied to rigid bodies.
+ ///
+ public PhysicsVolumeForceSpace relativeTorqueSpace = PhysicsVolumeForceSpace.Relative;
+
+ ///
+ /// The relative torque applied to rigid bodies.
+ ///
+ public Vector3 relativeTorque = new Vector3(0.0f, 0.0f, 0.0f);
+
+ ///
+ /// The gravity settings applied to rigid bodies inside the volume.
+ ///
+ public PhysicsVolumeGravityMode gravity = PhysicsVolumeGravityMode.None;
+
+ ///
+ /// The layer mask to limit the effects of the physics volume to specific layers.
+ ///
+ public LayerMask layer = -1;
+
+ ///
+ /// Whether to use a filter tag.
+ ///
+ public bool useFilterTag = false;
+
+ ///
+ /// The filter tag to limit the effects of the physics volume to specific tags.
+ ///
+ public string filterTag = "Untagged";
+
+ ///
+ /// The rigid bodies we are tracking as they entered the volume.
+ ///
+ private List rigidBodies;
+
+ ///
+ /// Called whenever the volume is enabled.
+ ///
+ private void OnEnable()
+ {
+ // time may have passed, reset our list of rigid bodies.
+ rigidBodies = new List();
+ }
+
+ private void FixedUpdate()
+ {
+ // iterate the tracked rigid bodies in reverse.
+ for (int i = rigidBodies.Count - 1; i >= 0; i--)
+ {
+ TrackedRigidbody trackedRigidbody = rigidBodies[i];
+ Rigidbody rigidbody = trackedRigidbody.rigidbody;
+ // if the rigid body was deleted, stop tracking it.
+ if (!rigidbody)
+ {
+ rigidBodies.RemoveAt(i);
+ continue;
+ }
+ // apply the force to the rigid body.
+ Vector3 appliedForce = forceSpace == PhysicsVolumeForceSpace.Relative ? transform.rotation * force : force;
+ switch (forceMode)
+ {
+ case PhysicsVolumeForceMode.Force:
+ rigidbody.AddForce(appliedForce, ForceMode.Force);
+ break;
+
+ case PhysicsVolumeForceMode.Impulse:
+ rigidbody.AddForce(appliedForce, ForceMode.Impulse);
+ break;
+
+ case PhysicsVolumeForceMode.VelocityChange:
+ rigidbody.AddForce(appliedForce, ForceMode.VelocityChange);
+ break;
+
+ case PhysicsVolumeForceMode.Acceleration:
+ rigidbody.AddForce(appliedForce, ForceMode.Acceleration);
+ break;
+ }
+ // apply the relative force to the rigid body.
+ Vector3 appliedRelativeForce = relativeForceSpace == PhysicsVolumeForceSpace.Relative ? transform.rotation * relativeForce : relativeForce;
+ switch (relativeForceMode)
+ {
+ case PhysicsVolumeForceMode.Force:
+ rigidbody.AddRelativeForce(appliedRelativeForce, ForceMode.Force);
+ break;
+
+ case PhysicsVolumeForceMode.Impulse:
+ rigidbody.AddRelativeForce(appliedRelativeForce, ForceMode.Impulse);
+ break;
+
+ case PhysicsVolumeForceMode.VelocityChange:
+ rigidbody.AddRelativeForce(appliedRelativeForce, ForceMode.VelocityChange);
+ break;
+
+ case PhysicsVolumeForceMode.Acceleration:
+ rigidbody.AddRelativeForce(appliedRelativeForce, ForceMode.Acceleration);
+ break;
+ }
+ // apply the torque to the rigid body.
+ Vector3 appliedTorque = torqueSpace == PhysicsVolumeForceSpace.Relative ? transform.rotation * torque : torque;
+ switch (torqueForceMode)
+ {
+ case PhysicsVolumeForceMode.Force:
+ rigidbody.AddTorque(appliedTorque, ForceMode.Force);
+ break;
+
+ case PhysicsVolumeForceMode.Impulse:
+ rigidbody.AddTorque(appliedTorque, ForceMode.Impulse);
+ break;
+
+ case PhysicsVolumeForceMode.VelocityChange:
+ rigidbody.AddTorque(appliedTorque, ForceMode.VelocityChange);
+ break;
+
+ case PhysicsVolumeForceMode.Acceleration:
+ rigidbody.AddTorque(appliedTorque, ForceMode.Acceleration);
+ break;
+ }
+ // apply the relative torque to the rigid body.
+ Vector3 appliedRelativeTorque = relativeTorqueSpace == PhysicsVolumeForceSpace.Relative ? transform.rotation * relativeTorque : relativeTorque;
+ switch (relativeTorqueForceMode)
+ {
+ case PhysicsVolumeForceMode.Force:
+ rigidbody.AddRelativeTorque(appliedRelativeTorque, ForceMode.Force);
+ break;
+
+ case PhysicsVolumeForceMode.Impulse:
+ rigidbody.AddRelativeTorque(appliedRelativeTorque, ForceMode.Impulse);
+ break;
+
+ case PhysicsVolumeForceMode.VelocityChange:
+ rigidbody.AddRelativeTorque(appliedRelativeTorque, ForceMode.VelocityChange);
+ break;
+
+ case PhysicsVolumeForceMode.Acceleration:
+ rigidbody.AddRelativeTorque(appliedRelativeTorque, ForceMode.Acceleration);
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Called when a collider enters the volume. We track any rigid bodies.
+ ///
+ /// The collider that entered the volume.
+ private void OnTriggerEnter(Collider other)
+ {
+ if (!other) return;
+ // apply the layer mask limit.
+ if (!layer.Contains(other.gameObject.layer)) return;
+ // apply the tag filter.
+ if (useFilterTag && other.tag != filterTag) return;
+ Rigidbody rigidbody = other.GetComponent();
+ if (!rigidbody) return;
+ if (rigidBodies.Find(r => r.rigidbody == rigidbody) == null)
+ rigidBodies.Add(new TrackedRigidbody(rigidbody));
+ // apply the gravity mode to the rigid body.
+ switch (gravity)
+ {
+ case PhysicsVolumeGravityMode.Enable:
+ rigidbody.useGravity = true;
+ break;
+
+ case PhysicsVolumeGravityMode.Disable:
+ case PhysicsVolumeGravityMode.ZeroGravity:
+ case PhysicsVolumeGravityMode.ZeroGravityRestore:
+ rigidbody.useGravity = false;
+ break;
+ }
+ }
+
+ ///
+ /// Called when a collider exits the volume. We stop tracking any rigid bodies.
+ ///
+ /// The collider that exited the volume.
+ private void OnTriggerExit(Collider other)
+ {
+ if (!other) return;
+ // apply the layer mask limit.
+ if (!layer.Contains(other.gameObject.layer)) return;
+ // apply the tag filter.
+ if (useFilterTag && other.tag != filterTag) return;
+ Rigidbody rigidbody = other.GetComponent();
+ if (!rigidbody) return;
+ int index = rigidBodies.FindIndex(r => r.rigidbody == rigidbody);
+ TrackedRigidbody trackedRigidbody = rigidBodies[index];
+ rigidBodies.RemoveAt(index);
+ // apply the gravity mode to the rigid body.
+ switch (gravity)
+ {
+ case PhysicsVolumeGravityMode.ZeroGravity:
+ rigidbody.useGravity = true;
+ break;
+
+ case PhysicsVolumeGravityMode.ZeroGravityRestore:
+ rigidbody.useGravity = trackedRigidbody.hadGravity;
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeComponent.cs.meta b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeComponent.cs.meta
new file mode 100644
index 00000000..6204f234
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeComponent.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 5b550fb7e5bb4424c95b8a18e0e1114c
+timeCreated: 1528723867
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceMode.cs b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceMode.cs
new file mode 100644
index 00000000..d72d19e3
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceMode.cs
@@ -0,0 +1,31 @@
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// The force modes for the .
+ ///
+ public enum PhysicsVolumeForceMode
+ {
+ ///
+ /// Don't do anything.
+ ///
+ None = 0,
+ ///
+ /// Add a continuous force to the rigidbody, using its mass.
+ ///
+ Force = 1,
+ ///
+ /// Add an instant force impulse to the rigidbody, using its mass.
+ ///
+ Impulse = 2,
+ ///
+ /// Add an instant velocity change to the rigidbody, ignoring its mass.
+ ///
+ VelocityChange = 3,
+ ///
+ /// Add a continuous acceleration to the rigidbody, ignoring its mass.
+ ///
+ Acceleration = 4
+ }
+}
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceMode.cs.meta b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceMode.cs.meta
new file mode 100644
index 00000000..382734ae
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceMode.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: e2b36b347843d0945bc7a376d8e4a9c4
+timeCreated: 1528726051
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceSpace.cs b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceSpace.cs
new file mode 100644
index 00000000..684ef6e8
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceSpace.cs
@@ -0,0 +1,20 @@
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// The force space modes for the .
+ ///
+ public enum PhysicsVolumeForceSpace
+ {
+ ///
+ /// Rotates physical forces relative to the volume brush.
+ ///
+ Relative = 0,
+
+ ///
+ /// The physical forces are applied in world space.
+ ///
+ World = 1
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceSpace.cs.meta b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceSpace.cs.meta
new file mode 100644
index 00000000..0dd69616
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeForceSpace.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: e184aeb6cc8b0634c988ccd8082970d4
+timeCreated: 1529326691
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeGravityMode.cs b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeGravityMode.cs
new file mode 100644
index 00000000..a2423954
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeGravityMode.cs
@@ -0,0 +1,31 @@
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// The gravity modes for the .
+ ///
+ public enum PhysicsVolumeGravityMode
+ {
+ ///
+ /// Don't do anything.
+ ///
+ None = 0,
+ ///
+ /// Enable gravity on the rigidbody.
+ ///
+ Enable = 1,
+ ///
+ /// Disable gravity on the rigidbody.
+ ///
+ Disable = 2,
+ ///
+ /// Disable gravity on the rigidbody while inside the volume, enable it on exit.
+ ///
+ ZeroGravity = 3,
+ ///
+ /// Disable gravity on the rigidbody while inside the volume, restore the original gravity settings on exit.
+ ///
+ ZeroGravityRestore = 4
+ }
+}
diff --git a/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeGravityMode.cs.meta b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeGravityMode.cs.meta
new file mode 100644
index 00000000..1c96891a
--- /dev/null
+++ b/Scripts/Brushes/Volumes/PhysicsVolume/PhysicsVolumeGravityMode.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: da2283e0ac6c6dd4a9c8e035e3a0e4b1
+timeCreated: 1528809026
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/TriggerVolume.meta b/Scripts/Brushes/Volumes/TriggerVolume.meta
new file mode 100644
index 00000000..d86aaee9
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b5efb8be9c19deb42b757217161dc369
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolume.cs b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolume.cs
new file mode 100644
index 00000000..dbfd9c36
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolume.cs
@@ -0,0 +1,217 @@
+#if UNITY_EDITOR || RUNTIME_CSG
+
+using System;
+using System.Linq;
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// Executes trigger logic when objects interact with the volume.
+ ///
+ ///
+ [Serializable]
+ public class TriggerVolume : Volume
+ {
+ ///
+ /// The trigger type, this is reserved for future use.
+ ///
+ [SerializeField]
+ public TriggerVolumeTriggerType triggerType = TriggerVolumeTriggerType.UnityEvent;
+
+ ///
+ /// Whether to use a filter tag.
+ ///
+ [SerializeField]
+ public bool useFilterTag = false;
+
+ ///
+ /// The filter tag to limit the colliders that can invoke the trigger.
+ ///
+ [SerializeField]
+ public string filterTag = "Untagged";
+
+ ///
+ /// The layer mask to limit the colliders that can invoke the trigger.
+ ///
+ [SerializeField]
+ public LayerMask layer = -1;
+
+ ///
+ /// Whether the trigger can only be instigated once.
+ ///
+ [SerializeField]
+ public bool triggerOnceOnly = false;
+
+ ///
+ /// The event called when a collider enters the trigger volume.
+ ///
+ [SerializeField]
+ public TriggerVolumeEvent onEnterEvent;
+
+ ///
+ /// The event called when a collider stays in the trigger volume.
+ ///
+ [SerializeField]
+ public TriggerVolumeEvent onStayEvent;
+
+ ///
+ /// The event called when a collider exits the trigger volume.
+ ///
+ [SerializeField]
+ public TriggerVolumeEvent onExitEvent;
+
+#if UNITY_EDITOR
+
+ ///
+ /// Gets the brush preview material shown in the editor.
+ ///
+ public override Material BrushPreviewMaterial
+ {
+ get
+ {
+ return (Material)SabreCSGResources.LoadObject("Materials/scsg_volume_trigger.mat");
+ }
+ }
+
+ ///
+ /// Called when the inspector GUI is drawn in the editor.
+ ///
+ /// The selected volumes in the editor (for multi-editing).
+ /// True if a property changed or else false.
+ public override bool OnInspectorGUI(Volume[] selectedVolumes)
+ {
+ var triggerVolumes = selectedVolumes.Cast();
+ bool invalidate = false;
+
+ GUILayout.BeginVertical("Box");
+ {
+ UnityEditor.EditorGUILayout.LabelField("Trigger Options", UnityEditor.EditorStyles.boldLabel);
+ GUILayout.Space(4);
+
+ UnityEditor.EditorGUI.indentLevel = 1;
+
+ GUILayout.BeginVertical();
+ {
+ // this is hidden so that we can introduce more trigger types in the future.
+
+ //TriggerVolumeTriggerType previousVolumeEventType;
+ //triggerType = (TriggerVolumeTriggerType)UnityEditor.EditorGUILayout.EnumPopup(new GUIContent("Trigger Type"), previousVolumeEventType = triggerType);
+ //if (triggerType != previousVolumeEventType)
+ //{
+ // foreach (TriggerVolume volume in triggerVolumes)
+ // volume.triggerType = triggerType;
+ // invalidate = true;
+ //}
+
+ LayerMask previousLayerMask;
+ layer = SabreGUILayout.LayerMaskField(new GUIContent("Layer Mask", "The layer mask to limit the colliders that can invoke the trigger."), (previousLayerMask = layer).value);
+ if (previousLayerMask != layer)
+ {
+ foreach (TriggerVolume volume in triggerVolumes)
+ volume.layer = layer;
+ invalidate = true;
+ }
+
+ bool previousUseFilterTag;
+ useFilterTag = UnityEditor.EditorGUILayout.Toggle(new GUIContent("Use Filter Tag", "Whether to use a filter tag."), previousUseFilterTag = useFilterTag);
+ if (useFilterTag != previousUseFilterTag)
+ {
+ foreach (TriggerVolume volume in triggerVolumes)
+ volume.useFilterTag = useFilterTag;
+ invalidate = true;
+ }
+
+ if (useFilterTag)
+ {
+ string previousFilterTag;
+ filterTag = UnityEditor.EditorGUILayout.TagField(new GUIContent("Filter Tag", "The filter tag to limit the colliders that can invoke the trigger."), previousFilterTag = filterTag);
+ if (filterTag != previousFilterTag)
+ {
+ foreach (TriggerVolume volume in triggerVolumes)
+ volume.filterTag = filterTag;
+ invalidate = true;
+ }
+ }
+
+ bool previousTriggerOnce;
+ triggerOnceOnly = UnityEditor.EditorGUILayout.Toggle(new GUIContent("Trigger Once Only", "Whether the trigger can only be instigated once."), previousTriggerOnce = triggerOnceOnly);
+ if (triggerOnceOnly != previousTriggerOnce)
+ {
+ foreach (TriggerVolume volume in triggerVolumes)
+ volume.triggerOnceOnly = triggerOnceOnly;
+ invalidate = true;
+ }
+ }
+ GUILayout.EndVertical();
+
+ UnityEditor.EditorGUI.indentLevel = 0;
+ }
+ GUILayout.EndVertical();
+
+ GUILayout.BeginVertical("Box");
+ {
+ UnityEditor.EditorGUILayout.LabelField("Trigger Events", UnityEditor.EditorStyles.boldLabel);
+ GUILayout.Space(4);
+
+ if (triggerType == TriggerVolumeTriggerType.UnityEvent)
+ {
+ UnityEditor.EditorGUI.indentLevel = 1;
+
+ GUILayout.BeginVertical();
+ {
+ UnityEditor.SerializedObject tv = new UnityEditor.SerializedObject(this);
+ UnityEditor.SerializedProperty prop1 = tv.FindProperty("onEnterEvent");
+ UnityEditor.SerializedProperty prop2 = tv.FindProperty("onStayEvent");
+ UnityEditor.SerializedProperty prop3 = tv.FindProperty("onExitEvent");
+
+ UnityEditor.EditorGUI.BeginChangeCheck();
+
+ UnityEditor.EditorGUILayout.PropertyField(prop1);
+ UnityEditor.EditorGUILayout.PropertyField(prop2);
+ UnityEditor.EditorGUILayout.PropertyField(prop3);
+
+ if (UnityEditor.EditorGUI.EndChangeCheck())
+ {
+ tv.ApplyModifiedProperties();
+ foreach (TriggerVolume volume in triggerVolumes)
+ {
+ volume.onEnterEvent = onEnterEvent;
+ volume.onStayEvent = onStayEvent;
+ volume.onExitEvent = onExitEvent;
+ }
+ invalidate = true;
+ }
+ }
+ GUILayout.EndVertical();
+
+ UnityEditor.EditorGUI.indentLevel = 0;
+ }
+ }
+ GUILayout.EndVertical();
+
+ return invalidate;
+ }
+
+#endif
+
+ ///
+ /// Called when the volume is created in the editor.
+ ///
+ /// The generated volume game object.
+ public override void OnCreateVolume(GameObject volume)
+ {
+ TriggerVolumeComponent tvc = volume.AddComponent();
+ tvc.triggerType = triggerType;
+ tvc.useFilterTag = useFilterTag;
+ tvc.filterTag = filterTag;
+ tvc.layer = layer;
+ tvc.triggerOnceOnly = triggerOnceOnly;
+ tvc.onEnterEvent = onEnterEvent;
+ tvc.onStayEvent = onStayEvent;
+ tvc.onExitEvent = onExitEvent;
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolume.cs.meta b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolume.cs.meta
new file mode 100644
index 00000000..e3e9412b
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolume.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 726fd47c6b33553448d7d01f2fc93703
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeComponent.cs b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeComponent.cs
new file mode 100644
index 00000000..2393276b
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeComponent.cs
@@ -0,0 +1,131 @@
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// Executes trigger logic when objects interact with the volume.
+ ///
+ ///
+ public class TriggerVolumeComponent : MonoBehaviour
+ {
+ ///
+ /// The trigger type, this is reserved for future use.
+ ///
+ public TriggerVolumeTriggerType triggerType = TriggerVolumeTriggerType.UnityEvent;
+
+ ///
+ /// Whether to use a filter tag.
+ ///
+ public bool useFilterTag = false;
+
+ ///
+ /// The filter tag to limit the colliders that can invoke the trigger.
+ ///
+ public string filterTag = "Untagged";
+
+ ///
+ /// The layer mask to limit the colliders that can invoke the trigger.
+ ///
+ public LayerMask layer = -1;
+
+ ///
+ /// Whether the trigger can only be instigated once.
+ ///
+ public bool triggerOnceOnly = false;
+
+ ///
+ /// The event called when a collider enters the trigger volume.
+ ///
+ public TriggerVolumeEvent onEnterEvent;
+
+ ///
+ /// The event called when a collider stays in the trigger volume.
+ ///
+ public TriggerVolumeEvent onStayEvent;
+
+ ///
+ /// The event called when a collider exits the trigger volume.
+ ///
+ public TriggerVolumeEvent onExitEvent;
+
+ ///
+ /// Whether the trigger can still be triggered (used with ).
+ ///
+ private bool canTrigger = true;
+
+ ///
+ /// Called when a collider enters the volume.
+ ///
+ /// The collider that entered the volume.
+ private void OnTriggerEnter(Collider other)
+ {
+ // ignore empty events.
+ if (onEnterEvent.GetPersistentEventCount() == 0) return;
+ // tag filter:
+ if (useFilterTag && other.tag != filterTag) return;
+ // layer filter:
+ if (!layer.Contains(other.gameObject.layer)) return;
+ // trigger once only:
+ if (!triggerOnceOnly) canTrigger = true;
+ if (!canTrigger) return;
+ if (triggerOnceOnly) canTrigger = false;
+
+ switch (triggerType)
+ {
+ case TriggerVolumeTriggerType.UnityEvent:
+ onEnterEvent.Invoke();
+ break;
+ }
+ }
+
+ ///
+ /// Called when a collider exits the volume.
+ ///
+ /// The collider that exited the volume.
+ private void OnTriggerExit(Collider other)
+ {
+ // ignore empty events.
+ if (onExitEvent.GetPersistentEventCount() == 0) return;
+ // tag filter:
+ if (useFilterTag && other.tag != filterTag) return;
+ // layer filter:
+ if (!layer.Contains(other.gameObject.layer)) return;
+ // trigger once only:
+ if (!triggerOnceOnly) canTrigger = true;
+ if (!canTrigger) return;
+ if (triggerOnceOnly) canTrigger = false;
+
+ switch (triggerType)
+ {
+ case TriggerVolumeTriggerType.UnityEvent:
+ onExitEvent.Invoke();
+ break;
+ }
+ }
+
+ ///
+ /// Called every frame while a collider stays inside the volume.
+ ///
+ /// The collider that is inside of the volume.
+ private void OnTriggerStay(Collider other)
+ {
+ // ignore empty events.
+ if (onStayEvent.GetPersistentEventCount() == 0) return;
+ // tag filter:
+ if (useFilterTag && other.tag != filterTag) return;
+ // layer filter:
+ if (!layer.Contains(other.gameObject.layer)) return;
+ // trigger once only:
+ if (!triggerOnceOnly) canTrigger = true;
+ if (!canTrigger) return;
+ if (triggerOnceOnly) canTrigger = false;
+
+ switch (triggerType)
+ {
+ case TriggerVolumeTriggerType.UnityEvent:
+ onStayEvent.Invoke();
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeComponent.cs.meta b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeComponent.cs.meta
new file mode 100644
index 00000000..c9456b9f
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeComponent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e3e265623b93290448278d14b10f1388
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeEvent.cs b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeEvent.cs
new file mode 100644
index 00000000..e7b206e0
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeEvent.cs
@@ -0,0 +1,13 @@
+using UnityEngine.Events;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// The unity event for the .
+ ///
+ ///
+ [System.Serializable]
+ public class TriggerVolumeEvent : UnityEvent
+ {
+ }
+}
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeEvent.cs.meta b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeEvent.cs.meta
new file mode 100644
index 00000000..ec302108
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeEvent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ee870a15e056055469ae62cf0878f226
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeTriggerType.cs b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeTriggerType.cs
new file mode 100644
index 00000000..fa32784a
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeTriggerType.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Sabresaurus.SabreCSG.Volumes
+{
+ ///
+ /// The trigger types for the .
+ ///
+ [Serializable]
+ public enum TriggerVolumeTriggerType
+ {
+ ///
+ /// Uses unity events to trigger things in the scene.
+ ///
+ UnityEvent = 0
+ };
+}
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeTriggerType.cs.meta b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeTriggerType.cs.meta
new file mode 100644
index 00000000..92d03cd0
--- /dev/null
+++ b/Scripts/Brushes/Volumes/TriggerVolume/TriggerVolumeTriggerType.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 4848777f5e2594544a1859c988a90f0b
+timeCreated: 1528731397
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Brushes/Volumes/Volume.cs b/Scripts/Brushes/Volumes/Volume.cs
new file mode 100644
index 00000000..b81c1491
--- /dev/null
+++ b/Scripts/Brushes/Volumes/Volume.cs
@@ -0,0 +1,83 @@
+#if UNITY_EDITOR || RUNTIME_CSG
+
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG
+{
+ ///
+ /// Volumes are custom programmable triggers in the shape of primitive brushes.
+ ///
+ [System.Serializable]
+ public class Volume : ScriptableObject
+ {
+#if UNITY_EDITOR
+
+ ///
+ /// Gets the brush preview material shown in the editor.
+ ///
+ /// The volume material.
+ public virtual Material BrushPreviewMaterial
+ {
+ get
+ {
+ return SabreCSGResources.GetVolumeMaterial();
+ }
+ }
+
+ ///
+ /// Called when the inspector GUI is drawn in the editor.
+ ///
+ /// The selected volumes in the editor (for multi-editing).
+ /// True if a property changed or else false.
+ public virtual bool OnInspectorGUI(Volume[] selectedVolumes)
+ {
+ return false;
+ }
+
+ ///
+ /// Searches the main C# assembly for volume types that can be instantiated.
+ ///
+ /// All matched volume types.
+ public static List FindAllInAssembly()
+ {
+ List matchedTypes = new List();
+
+ Assembly[] allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
+ foreach (Assembly assembly in allAssemblies)
+ {
+ // walk through all the types in the main assembly
+ if (assembly.FullName.StartsWith("Assembly-CSharp"))
+ {
+ Type[] types = assembly.GetTypes();
+
+ for (int i = 0; i < types.Length; i++)
+ {
+ // if the type inherits from volume and is not abstract
+ if (!types[i].IsAbstract && types[i].IsSubclassOf(typeof(Volume)))
+ {
+ // valid volume type found!
+ matchedTypes.Add(types[i]);
+ }
+ }
+ }
+ }
+
+ return matchedTypes;
+ }
+
+#endif
+
+ ///
+ /// Called when the volume is created in the editor.
+ ///
+ /// The generated volume game object.
+ public virtual void OnCreateVolume(GameObject volume)
+ {
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Scripts/Brushes/Volumes/Volume.cs.meta b/Scripts/Brushes/Volumes/Volume.cs.meta
new file mode 100644
index 00000000..83530100
--- /dev/null
+++ b/Scripts/Brushes/Volumes/Volume.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: bcc6b2f51abf491459662ef7cbee1028
+timeCreated: 1528583973
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/CSGModel.cs b/Scripts/CSGModel.cs
index c5add3bc..68d76e88 100644
--- a/Scripts/CSGModel.cs
+++ b/Scripts/CSGModel.cs
@@ -414,7 +414,15 @@ public void OnSceneGUI(SceneView sceneView)
if (Selection.Contains(brushGameObject))
{
- if (brushes[brushIndex].IsNoCSG)
+ if (brushes[brushIndex].Mode == CSGMode.Volume)
+ {
+ outlineColor = new Color32(192, 192, 192, 255);
+ }
+ else if (!brushes[brushIndex].IsVisible)
+ {
+ outlineColor = new Color(0.6f, 1.0f, 0.6f);
+ }
+ else if (brushes[brushIndex].IsNoCSG)
{
outlineColor = new Color(1f, 0.6f, 1.0f);
}
@@ -432,7 +440,15 @@ public void OnSceneGUI(SceneView sceneView)
}
else if (CurrentSettings.BrushesVisible)
{
- if (brushes[brushIndex].IsNoCSG)
+ if (brushes[brushIndex].Mode == CSGMode.Volume)
+ {
+ outlineColor = new Color32(0, 0, 0, 255);
+ }
+ else if (!brushes[brushIndex].IsVisible)
+ {
+ outlineColor = new Color(0.0f, 1.0f, 0.0f);
+ }
+ else if (brushes[brushIndex].IsNoCSG)
{
outlineColor = new Color(0.8f, 0.3f, 1.0f);
}
@@ -981,13 +997,18 @@ private void OnSelectionChanged()
for (int i = 0; i < Selection.gameObjects.Length; i++)
{
// Skip any selected prefabs in the project window
+#if UNITY_2018_2
+ if(PrefabUtility.GetCorrespondingObjectFromSource(Selection.gameObjects[i]) == null
+#else
if (PrefabUtility.GetPrefabParent(Selection.gameObjects[i]) == null
- && PrefabUtility.GetPrefabObject(Selection.gameObjects[i].transform) != null)
- {
- continue;
- }
+#endif
+ && PrefabUtility.GetPrefabObject(Selection.gameObjects[i].transform) != null)
+
+ {
+ continue;
+ }
- PrimitiveBrush primitiveBrush = Selection.gameObjects[i].GetComponent();
+ PrimitiveBrush primitiveBrush = Selection.gameObjects[i].GetComponent();
CSGModel csgModel = Selection.gameObjects[i].GetComponent();
if (csgModel == null)
@@ -1128,7 +1149,7 @@ private void OnSelectionChanged()
#endif
- public void SetLastSelectedBrush(Brush brush)
+ public void SetLastSelectedBrush(Brush brush)
{
lastSelectedBrush = brush;
}
@@ -1181,6 +1202,14 @@ private void OnHierarchyItemGUI(int instanceID, Rect drawRect)
{
Graphics.DrawTexture(drawRect, SabreCSGResources.GroupIconTexture, iconMaterial);
}
+ else if (brushBase.Mode == CSGMode.Volume)
+ {
+ Graphics.DrawTexture(drawRect, SabreCSGResources.VolumeIconTexture, iconMaterial);
+ }
+ else if (!brushBase.IsVisible)
+ {
+ Graphics.DrawTexture(drawRect, SabreCSGResources.CollisionIconTexture, iconMaterial);
+ }
else if (brushBase.IsNoCSG)
{
Graphics.DrawTexture(drawRect, SabreCSGResources.NoCSGIconTexture, iconMaterial);
@@ -1713,6 +1742,22 @@ public static void UpdateAllBrushesVisibility()
}
}
+ public static void RebuildAllVolumes()
+ {
+ CSGModel[] csgModels = Resources.FindObjectsOfTypeAll();
+ for (int i = 0; i < csgModels.Length; i++)
+ {
+ List brushes = csgModels[i].brushes;
+ for (int j = 0; j < brushes.Count; j++)
+ {
+ if (brushes[j] != null)
+ {
+ brushes[j].RebuildVolume();
+ }
+ }
+ }
+ }
+
public override Material GetDefaultFallbackMaterial()
{
if (!Application.isPlaying)
@@ -1968,10 +2013,28 @@ private static void CleanupForBuild(Transform csgModelTransform)
Transform meshGroup = csgModelTransform.Find("MeshGroup");
if (meshGroup != null)
{
+ // If the CSG Model is disabled we also disable the mesh group.
+ meshGroup.gameObject.SetActive(csgModelTransform.gameObject.activeSelf);
// Reanchor the meshes to the parent of the CSG Model
meshGroup.SetParent(csgModelTransform.parent, true);
}
+ // find all built volumes:
+ Transform[] volumes = csgModelTransform.FindChildren(Constants.GameObjectVolumeComponentIdentifier);
+ for (int i = 0; i < volumes.Length; i++)
+ {
+ // make sure they are visible and editable again.
+ volumes[i].gameObject.hideFlags = HideFlags.None;
+ // give them a more recognizable name.
+ volumes[i].name = volumes[i].parent.name.Replace(" Brush ", " Volume ");
+ if (meshGroup != null)
+ // Reanchor the volumes to the mesh group.
+ volumes[i].SetParent(meshGroup, true);
+ else
+ // Reanchor the volumes to the parent of the CSG Model
+ volumes[i].SetParent(csgModelTransform.parent, true);
+ }
+
// Remove the CSG Model and its brushes
DestroyImmediate(csgModelTransform.gameObject);
}
@@ -1998,4 +2061,4 @@ void OnDrawGizmosSelected()
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/CSGModelBase.cs b/Scripts/CSGModelBase.cs
index 00d4468c..377d0ee5 100644
--- a/Scripts/CSGModelBase.cs
+++ b/Scripts/CSGModelBase.cs
@@ -15,7 +15,7 @@ namespace Sabresaurus.SabreCSG
// [ExecuteInEditMode]
public class CSGModelBase : MonoBehaviour
{
- public const string VERSION_STRING = "1.6.3";
+ public const string VERSION_STRING = "1.7.2";
protected const string DEFAULT_FALLBACK_MATERIAL_PATH = "Materials/Default_Map";
// Limit to how many vertices a Unity mesh can hold, before it must be split into a second mesh (just under 2^16)
diff --git a/Scripts/Compatibility/Editor.meta b/Scripts/Compatibility/Editor.meta
new file mode 100644
index 00000000..2d9221c0
--- /dev/null
+++ b/Scripts/Compatibility/Editor.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 1c95ca27d9f00a34892a9b9824c9b519
+folderAsset: yes
+timeCreated: 1528787041
+licenseType: Free
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Compatibility/Editor/UnityEventDrawer.cs b/Scripts/Compatibility/Editor/UnityEventDrawer.cs
new file mode 100644
index 00000000..51b32687
--- /dev/null
+++ b/Scripts/Compatibility/Editor/UnityEventDrawer.cs
@@ -0,0 +1,762 @@
+#if !UNITY_2018_OR_NEWER
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using UnityEngine;
+using UnityEditor;
+using UnityEngine.Events;
+using Object = UnityEngine.Object;
+
+namespace UnityEditorInternal
+{
+ ///
+ /// A complete reimplementation of the UnityEvent drawer from 2018 for older Unity versions.
+ /// Problem with the original one is that it can't save in our volumes due to a bug.
+ /// Specifically:
+ /// Editor: Fixed bug where UnityEvent drawer would not synchronize correctly when displayed using multiple different serialized data streams, such as when displayed in two different windows at once. (974069)
+ ///
+ [CustomPropertyDrawer(typeof(UnityEvent), true)]
+ public class UnityEventDrawer : PropertyDrawer
+ {
+ public const float kSingleLineHeight = 16.0f;
+ public const int kControlVerticalSpacing = 2;
+ public const float kSpacing = 5f;
+
+ public static GUIContent TrTextContent(string text, string tooltip = null, Texture icon = null)
+ {
+ //string key = string.Format("{0}|{1}", text ?? "", tooltip ?? "");
+ return new GUIContent(text, icon, tooltip);
+ }
+
+
+
+
+ protected class State
+ {
+ internal ReorderableList m_ReorderableList;
+ public int lastSelectedIndex;
+ }
+
+ private const string kNoFunctionString = "No Function";
+
+ //Persistent Listener Paths
+ private const string kInstancePath = "m_Target";
+ private const string kCallStatePath = "m_CallState";
+ private const string kArgumentsPath = "m_Arguments";
+ private const string kModePath = "m_Mode";
+ private const string kMethodNamePath = "m_MethodName";
+
+ //ArgumentCache paths
+ private const string kFloatArgument = "m_FloatArgument";
+ private const string kIntArgument = "m_IntArgument";
+ private const string kObjectArgument = "m_ObjectArgument";
+ private const string kStringArgument = "m_StringArgument";
+ private const string kBoolArgument = "m_BoolArgument";
+ private const string kObjectArgumentAssemblyTypeName = "m_ObjectArgumentAssemblyTypeName";
+
+ string m_Text;
+ UnityEventBase m_DummyEvent;
+ SerializedProperty m_Prop;
+ SerializedProperty m_ListenersArray;
+
+ const int kExtraSpacing = 9;
+
+ //State:
+ ReorderableList m_ReorderableList;
+ int m_LastSelectedIndex;
+ Dictionary m_States = new Dictionary();
+
+ static string GetEventParams(UnityEventBase evt)
+ {
+ MethodInfo dynMethod = typeof(UnityEventBase).GetMethod("FindMethod", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(object), typeof(PersistentListenerMode), typeof(Type) }, new ParameterModifier[] { }); // BindingFlags.NonPublic | BindingFlags.Instance,
+ MethodInfo methodInfo = (MethodInfo)dynMethod.Invoke(evt, new object[] { "Invoke", evt, PersistentListenerMode.EventDefined, null });
+ // HENRY: evt.FindMethod("Invoke", evt, PersistentListenerMode.EventDefined, null);
+
+ var sb = new StringBuilder();
+ sb.Append(" (");
+
+ var types = methodInfo.GetParameters().Select(x => x.ParameterType).ToArray();
+ for (int i = 0; i < types.Length; i++)
+ {
+ sb.Append(types[i].Name);
+ if (i < types.Length - 1)
+ {
+ sb.Append(", ");
+ }
+ }
+ sb.Append(")");
+ return sb.ToString();
+ }
+
+ private State GetState(SerializedProperty prop)
+ {
+ State state;
+ string key = prop.propertyPath;
+ m_States.TryGetValue(key, out state);
+ // ensure the cached SerializedProperty is synchronized (case 974069)
+ if (state == null || state.m_ReorderableList.serializedProperty.serializedObject != prop.serializedObject)
+ {
+ if (state == null)
+ state = new State();
+
+ SerializedProperty listenersArray = prop.FindPropertyRelative("m_PersistentCalls.m_Calls");
+ state.m_ReorderableList = new ReorderableList(prop.serializedObject, listenersArray, false, true, true, true);
+ state.m_ReorderableList.drawHeaderCallback = DrawEventHeader;
+ state.m_ReorderableList.drawElementCallback = DrawEventListener;
+ state.m_ReorderableList.onSelectCallback = SelectEventListener;
+ state.m_ReorderableList.onReorderCallback = EndDragChild;
+ state.m_ReorderableList.onAddCallback = AddEventListener;
+ state.m_ReorderableList.onRemoveCallback = RemoveButton;
+ // Two standard lines with standard spacing between and extra spacing below to better separate items visually.
+ state.m_ReorderableList.elementHeight = kSingleLineHeight * 2 + kControlVerticalSpacing + kExtraSpacing;
+ // HENRY: state.m_ReorderableList.elementHeight = EditorGUI.kSingleLineHeight * 2 + EditorGUI.kControlVerticalSpacing + kExtraSpacing;
+
+ m_States[key] = state;
+ }
+ return state;
+ }
+
+ private State RestoreState(SerializedProperty property)
+ {
+ State state = GetState(property);
+
+ m_ListenersArray = state.m_ReorderableList.serializedProperty;
+ m_ReorderableList = state.m_ReorderableList;
+ m_LastSelectedIndex = state.lastSelectedIndex;
+ m_ReorderableList.index = m_LastSelectedIndex;
+
+ return state;
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ m_Prop = property;
+ m_Text = label.text;
+
+ State state = RestoreState(property);
+
+ OnGUI(position);
+ state.lastSelectedIndex = m_LastSelectedIndex;
+ }
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ //TODO: Also we need to have a constructor or initializer called for this property Drawer, before OnGUI or GetPropertyHeight
+ //otherwise, we get Restore the State twice, once here and agai in OnGUI. Maybe we should only do it here?
+ RestoreState(property);
+
+ float height = 0f;
+ if (m_ReorderableList != null)
+ {
+ height = m_ReorderableList.GetHeight();
+ }
+ return height;
+ }
+
+ public void OnGUI(Rect position)
+ {
+ if (m_ListenersArray == null || !m_ListenersArray.isArray)
+ return;
+
+ m_DummyEvent = GetDummyEvent(m_Prop);
+ if (m_DummyEvent == null)
+ return;
+
+ if (m_ReorderableList != null)
+ {
+ var oldIdentLevel = EditorGUI.indentLevel;
+ EditorGUI.indentLevel = 0;
+ m_ReorderableList.DoList(position);
+ EditorGUI.indentLevel = oldIdentLevel;
+ }
+ }
+
+ protected virtual void DrawEventHeader(Rect headerRect)
+ {
+ headerRect.height = 16;
+
+ string text = (string.IsNullOrEmpty(m_Text) ? "Event" : m_Text) + GetEventParams(m_DummyEvent);
+ GUI.Label(headerRect, text);
+ }
+
+ static PersistentListenerMode GetMode(SerializedProperty mode)
+ {
+ return (PersistentListenerMode)mode.enumValueIndex;
+ }
+
+ void DrawEventListener(Rect rect, int index, bool isactive, bool isfocused)
+ {
+ var pListener = m_ListenersArray.GetArrayElementAtIndex(index);
+
+ rect.y++;
+ Rect[] subRects = GetRowRects(rect);
+ Rect enabledRect = subRects[0];
+ Rect goRect = subRects[1];
+ Rect functionRect = subRects[2];
+ Rect argRect = subRects[3];
+
+ // find the current event target...
+ var callState = pListener.FindPropertyRelative(kCallStatePath);
+ var mode = pListener.FindPropertyRelative(kModePath);
+ var arguments = pListener.FindPropertyRelative(kArgumentsPath);
+ var listenerTarget = pListener.FindPropertyRelative(kInstancePath);
+ var methodName = pListener.FindPropertyRelative(kMethodNamePath);
+
+ Color c = GUI.backgroundColor;
+ GUI.backgroundColor = Color.white;
+
+ EditorGUI.PropertyField(enabledRect, callState, GUIContent.none);
+
+ EditorGUI.BeginChangeCheck();
+ {
+ GUI.Box(goRect, GUIContent.none);
+ EditorGUI.PropertyField(goRect, listenerTarget, GUIContent.none);
+ if (EditorGUI.EndChangeCheck())
+ methodName.stringValue = null;
+ }
+
+ SerializedProperty argument;
+ var modeEnum = GetMode(mode);
+ //only allow argument if we have a valid target / method
+ if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue))
+ modeEnum = PersistentListenerMode.Void;
+
+ switch (modeEnum)
+ {
+ case PersistentListenerMode.Float:
+ argument = arguments.FindPropertyRelative(kFloatArgument);
+ break;
+ case PersistentListenerMode.Int:
+ argument = arguments.FindPropertyRelative(kIntArgument);
+ break;
+ case PersistentListenerMode.Object:
+ argument = arguments.FindPropertyRelative(kObjectArgument);
+ break;
+ case PersistentListenerMode.String:
+ argument = arguments.FindPropertyRelative(kStringArgument);
+ break;
+ case PersistentListenerMode.Bool:
+ argument = arguments.FindPropertyRelative(kBoolArgument);
+ break;
+ default:
+ argument = arguments.FindPropertyRelative(kIntArgument);
+ break;
+ }
+
+ var desiredArgTypeName = arguments.FindPropertyRelative(kObjectArgumentAssemblyTypeName).stringValue;
+ var desiredType = typeof(Object);
+ if (!string.IsNullOrEmpty(desiredArgTypeName))
+ desiredType = Type.GetType(desiredArgTypeName, false) ?? typeof(Object);
+
+ if (modeEnum == PersistentListenerMode.Object)
+ {
+ EditorGUI.BeginChangeCheck();
+ var result = EditorGUI.ObjectField(argRect, GUIContent.none, argument.objectReferenceValue, desiredType, true);
+ if (EditorGUI.EndChangeCheck())
+ argument.objectReferenceValue = result;
+ }
+ else if (modeEnum != PersistentListenerMode.Void && modeEnum != PersistentListenerMode.EventDefined)
+ EditorGUI.PropertyField(argRect, argument, GUIContent.none);
+
+#if UNITY_5
+ using (new EditorGUI.DisabledGroupScope(listenerTarget.objectReferenceValue == null))
+#else
+ using (new EditorGUI.DisabledScope(listenerTarget.objectReferenceValue == null))
+#endif
+ {
+ EditorGUI.BeginProperty(functionRect, GUIContent.none, methodName);
+ {
+ GUIContent buttonContent;
+ if (EditorGUI.showMixedValue)
+ {
+ // HENRY: GUIContent s_MixedValueContent = EditorGUIUtility.TrTextContent("—", "Mixed Values", (Texture)null);
+ GUIContent s_MixedValueContent = TrTextContent("—", "Mixed Values", (Texture)null);
+ buttonContent = s_MixedValueContent;
+ // HENRY: buttonContent = EditorGUI.mixedValueContent;
+ }
+ else
+ {
+ var buttonLabel = new StringBuilder();
+ if (listenerTarget.objectReferenceValue == null || string.IsNullOrEmpty(methodName.stringValue))
+ {
+ buttonLabel.Append(kNoFunctionString);
+ }
+ else if (!IsPersistantListenerValid(m_DummyEvent, methodName.stringValue, listenerTarget.objectReferenceValue, GetMode(mode), desiredType))
+ {
+ var instanceString = "UnknownComponent";
+ var instance = listenerTarget.objectReferenceValue;
+ if (instance != null)
+ instanceString = instance.GetType().Name;
+
+ buttonLabel.Append(string.Format("", instanceString, methodName.stringValue));
+ }
+ else
+ {
+ buttonLabel.Append(listenerTarget.objectReferenceValue.GetType().Name);
+
+ if (!string.IsNullOrEmpty(methodName.stringValue))
+ {
+ buttonLabel.Append(".");
+ if (methodName.stringValue.StartsWith("set_"))
+ buttonLabel.Append(methodName.stringValue.Substring(4));
+ else
+ buttonLabel.Append(methodName.stringValue);
+ }
+ }
+
+ //
+ // call GUIContent.Temp
+ //
+ //MethodInfo dynMethod = typeof(UnityEventBase).GetMethod("FindMethod", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(object), typeof(PersistentListenerMode), typeof(Type) }, new ParameterModifier[] { }); // BindingFlags.NonPublic | BindingFlags.Instance,
+ buttonContent = (GUIContent)typeof(GUIContent).GetMethod("Temp", BindingFlags.NonPublic | BindingFlags.Static, null, new Type[] { typeof(string) }, new ParameterModifier[] { }).Invoke(null, new object[] { buttonLabel.ToString() });
+
+ // HENRY: buttonContent = GUIContent.Temp(buttonLabel.ToString());
+ }
+
+ if (GUI.Button(functionRect, buttonContent, EditorStyles.popup))
+ BuildPopupList(listenerTarget.objectReferenceValue, m_DummyEvent, pListener).DropDown(functionRect);
+ }
+ EditorGUI.EndProperty();
+ }
+ GUI.backgroundColor = c;
+ }
+
+ Rect[] GetRowRects(Rect rect)
+ {
+ Rect[] rects = new Rect[4];
+
+ rect.height = kSingleLineHeight;
+ // HENRY: rect.height = EditorGUI.kSingleLineHeight;
+ rect.y += 2;
+
+ Rect enabledRect = rect;
+ enabledRect.width *= 0.3f;
+
+ Rect goRect = enabledRect;
+ goRect.y += kSingleLineHeight + kControlVerticalSpacing;
+ // HENRY: goRect.y += EditorGUIUtility.singleLineHeight + EditorGUI.kControlVerticalSpacing;
+
+ Rect functionRect = rect;
+
+ functionRect.xMin = goRect.xMax + kSpacing;
+ // HENRY: functionRect.xMin = goRect.xMax + EditorGUI.kSpacing;
+
+ Rect argRect = functionRect;
+ argRect.y += kSingleLineHeight + kControlVerticalSpacing;
+ // HENRY: argRect.y += EditorGUIUtility.singleLineHeight + EditorGUI.kControlVerticalSpacing;
+
+ rects[0] = enabledRect;
+ rects[1] = goRect;
+ rects[2] = functionRect;
+ rects[3] = argRect;
+ return rects;
+ }
+
+ void RemoveButton(ReorderableList list)
+ {
+ ReorderableList.defaultBehaviours.DoRemoveButton(list);
+ m_LastSelectedIndex = list.index;
+ }
+
+ private void AddEventListener(ReorderableList list)
+ {
+ if (m_ListenersArray.hasMultipleDifferentValues)
+ {
+ //When increasing a multi-selection array using Serialized Property
+ //Data can be overwritten if there is mixed values.
+ //The Serialization system applies the Serialized data of one object, to all other objects in the selection.
+ //We handle this case here, by creating a SerializedObject for each object.
+ //Case 639025.
+ foreach (var targetObject in m_ListenersArray.serializedObject.targetObjects)
+ {
+ var temSerialziedObject = new SerializedObject(targetObject);
+ var listenerArrayProperty = temSerialziedObject.FindProperty(m_ListenersArray.propertyPath);
+ listenerArrayProperty.arraySize += 1;
+ temSerialziedObject.ApplyModifiedProperties();
+ }
+ m_ListenersArray.serializedObject.SetIsDifferentCacheDirty();
+ m_ListenersArray.serializedObject.Update();
+ list.index = list.serializedProperty.arraySize - 1;
+ }
+ else
+ {
+ ReorderableList.defaultBehaviours.DoAddButton(list);
+ }
+
+ m_LastSelectedIndex = list.index;
+ var pListener = m_ListenersArray.GetArrayElementAtIndex(list.index);
+
+ var callState = pListener.FindPropertyRelative(kCallStatePath);
+ var listenerTarget = pListener.FindPropertyRelative(kInstancePath);
+ var methodName = pListener.FindPropertyRelative(kMethodNamePath);
+ var mode = pListener.FindPropertyRelative(kModePath);
+ var arguments = pListener.FindPropertyRelative(kArgumentsPath);
+
+ callState.enumValueIndex = (int)UnityEventCallState.RuntimeOnly;
+ listenerTarget.objectReferenceValue = null;
+ methodName.stringValue = null;
+ mode.enumValueIndex = (int)PersistentListenerMode.Void;
+ arguments.FindPropertyRelative(kFloatArgument).floatValue = 0;
+ arguments.FindPropertyRelative(kIntArgument).intValue = 0;
+ arguments.FindPropertyRelative(kObjectArgument).objectReferenceValue = null;
+ arguments.FindPropertyRelative(kStringArgument).stringValue = null;
+ arguments.FindPropertyRelative(kObjectArgumentAssemblyTypeName).stringValue = null;
+ }
+
+ void SelectEventListener(ReorderableList list)
+ {
+ m_LastSelectedIndex = list.index;
+ }
+
+ void EndDragChild(ReorderableList list)
+ {
+ m_LastSelectedIndex = list.index;
+ }
+
+ static UnityEventBase GetDummyEvent(SerializedProperty prop)
+ {
+ // Create dummy instance of this type... we need it for function validation ect
+ var typeName = prop.FindPropertyRelative("m_TypeName").stringValue;
+ Type type = Type.GetType(typeName, false);
+ if (type == null)
+ return new UnityEvent();
+ return Activator.CreateInstance(type) as UnityEventBase;
+ }
+
+ struct ValidMethodMap
+ {
+ public Object target;
+ public MethodInfo methodInfo;
+ public PersistentListenerMode mode;
+ }
+
+ static IEnumerable CalculateMethodMap(Object target, Type[] t, bool allowSubclasses)
+ {
+ var validMethods = new List();
+ if (target == null || t == null)
+ return validMethods;
+
+ // find the methods on the behaviour that match the signature
+ Type componentType = target.GetType();
+ var componentMethods = componentType.GetMethods().Where(x => !x.IsSpecialName).ToList();
+
+ var wantedProperties = componentType.GetProperties().AsEnumerable();
+ wantedProperties = wantedProperties.Where(x => x.GetCustomAttributes(typeof(ObsoleteAttribute), true).Length == 0 && x.GetSetMethod() != null);
+ componentMethods.AddRange(wantedProperties.Select(x => x.GetSetMethod()));
+
+ foreach (var componentMethod in componentMethods)
+ {
+ //Debug.Log ("Method: " + componentMethod);
+ // if the argument length is not the same, no match
+ var componentParamaters = componentMethod.GetParameters();
+ if (componentParamaters.Length != t.Length)
+ continue;
+
+ // Don't show obsolete methods.
+ if (componentMethod.GetCustomAttributes(typeof(ObsoleteAttribute), true).Length > 0)
+ continue;
+
+ if (componentMethod.ReturnType != typeof(void))
+ continue;
+
+ // if the argument types do not match, no match
+ bool paramatersMatch = true;
+ for (int i = 0; i < t.Length; i++)
+ {
+ if (!componentParamaters[i].ParameterType.IsAssignableFrom(t[i]))
+ paramatersMatch = false;
+
+ if (allowSubclasses && t[i].IsAssignableFrom(componentParamaters[i].ParameterType))
+ paramatersMatch = true;
+ }
+
+ // valid method
+ if (paramatersMatch)
+ {
+ var vmm = new ValidMethodMap();
+ vmm.target = target;
+ vmm.methodInfo = componentMethod;
+ validMethods.Add(vmm);
+ }
+ }
+ return validMethods;
+ }
+
+ public static bool IsPersistantListenerValid(UnityEventBase dummyEvent, string methodName, Object uObject, PersistentListenerMode modeEnum, Type argumentType)
+ {
+ if (uObject == null || string.IsNullOrEmpty(methodName))
+ return false;
+
+ //
+ // call evt.FindMethod
+ //
+ MethodInfo dynMethod = typeof(UnityEventBase).GetMethod("FindMethod", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(object), typeof(PersistentListenerMode), typeof(Type) }, new ParameterModifier[] { }); // BindingFlags.NonPublic | BindingFlags.Instance,
+
+ MethodInfo info = (MethodInfo)dynMethod.Invoke(dummyEvent, new object[] { methodName, uObject, modeEnum, argumentType });
+ var methodInfo = info;// dummyEvent.FindMethod(methodName, uObject, modeEnum, argumentType) != null;
+
+ return methodInfo != null;
+ }
+
+ static GenericMenu BuildPopupList(Object target, UnityEventBase dummyEvent, SerializedProperty listener)
+ {
+ //special case for components... we want all the game objects targets there!
+ var targetToUse = target;
+ if (targetToUse is Component)
+ targetToUse = (target as Component).gameObject;
+
+ // find the current event target...
+ var methodName = listener.FindPropertyRelative(kMethodNamePath);
+
+ var menu = new GenericMenu();
+ menu.AddItem(new GUIContent(kNoFunctionString),
+ string.IsNullOrEmpty(methodName.stringValue),
+ ClearEventFunction,
+ new UnityEventFunction(listener, null, null, PersistentListenerMode.EventDefined));
+
+ if (targetToUse == null)
+ return menu;
+
+ menu.AddSeparator("");
+
+ // figure out the signature of this delegate...
+ // The property at this stage points to the 'container' and has the field name
+ Type delegateType = dummyEvent.GetType();
+
+ // check out the signature of invoke as this is the callback!
+ MethodInfo delegateMethod = delegateType.GetMethod("Invoke");
+ var delegateArgumentsTypes = delegateMethod.GetParameters().Select(x => x.ParameterType).ToArray();
+
+ GeneratePopUpForType(menu, targetToUse, false, listener, delegateArgumentsTypes);
+ if (targetToUse is GameObject)
+ {
+ Component[] comps = (targetToUse as GameObject).GetComponents();
+ var duplicateNames = comps.Where(c => c != null).Select(c => c.GetType().Name).GroupBy(x => x).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
+ foreach (Component comp in comps)
+ {
+ if (comp == null)
+ continue;
+
+ GeneratePopUpForType(menu, comp, duplicateNames.Contains(comp.GetType().Name), listener, delegateArgumentsTypes);
+ }
+ }
+
+ return menu;
+ }
+
+ private static void GeneratePopUpForType(GenericMenu menu, Object target, bool useFullTargetName, SerializedProperty listener, Type[] delegateArgumentsTypes)
+ {
+ var methods = new List();
+ string targetName = useFullTargetName ? target.GetType().FullName : target.GetType().Name;
+
+ bool didAddDynamic = false;
+
+ // skip 'void' event defined on the GUI as we have a void prebuilt type!
+ if (delegateArgumentsTypes.Length != 0)
+ {
+ GetMethodsForTargetAndMode(target, delegateArgumentsTypes, methods, PersistentListenerMode.EventDefined);
+ if (methods.Count > 0)
+ {
+ menu.AddDisabledItem(new GUIContent(targetName + "/Dynamic " + string.Join(", ", delegateArgumentsTypes.Select(e => GetTypeName(e)).ToArray())));
+ AddMethodsToMenu(menu, listener, methods, targetName);
+ didAddDynamic = true;
+ }
+ }
+
+ methods.Clear();
+ GetMethodsForTargetAndMode(target, new[] { typeof(float) }, methods, PersistentListenerMode.Float);
+ GetMethodsForTargetAndMode(target, new[] { typeof(int) }, methods, PersistentListenerMode.Int);
+ GetMethodsForTargetAndMode(target, new[] { typeof(string) }, methods, PersistentListenerMode.String);
+ GetMethodsForTargetAndMode(target, new[] { typeof(bool) }, methods, PersistentListenerMode.Bool);
+ GetMethodsForTargetAndMode(target, new[] { typeof(Object) }, methods, PersistentListenerMode.Object);
+ GetMethodsForTargetAndMode(target, new Type[] { }, methods, PersistentListenerMode.Void);
+ if (methods.Count > 0)
+ {
+ if (didAddDynamic)
+ // AddSeperator doesn't seem to work for sub-menus, so we have to use this workaround instead of a proper separator for now.
+ menu.AddItem(new GUIContent(targetName + "/ "), false, null);
+ if (delegateArgumentsTypes.Length != 0)
+ menu.AddDisabledItem(new GUIContent(targetName + "/Static Parameters"));
+ AddMethodsToMenu(menu, listener, methods, targetName);
+ }
+ }
+
+ private static void AddMethodsToMenu(GenericMenu menu, SerializedProperty listener, List methods, string targetName)
+ {
+ // Note: sorting by a bool in OrderBy doesn't seem to work for some reason, so using numbers explicitly.
+ IEnumerable orderedMethods = methods.OrderBy(e => e.methodInfo.Name.StartsWith("set_") ? 0 : 1).ThenBy(e => e.methodInfo.Name);
+ foreach (var validMethod in orderedMethods)
+ AddFunctionsForScript(menu, listener, validMethod, targetName);
+ }
+
+ private static void GetMethodsForTargetAndMode(Object target, Type[] delegateArgumentsTypes, List methods, PersistentListenerMode mode)
+ {
+ IEnumerable newMethods = CalculateMethodMap(target, delegateArgumentsTypes, mode == PersistentListenerMode.Object);
+ foreach (var m in newMethods)
+ {
+ var method = m;
+ method.mode = mode;
+ methods.Add(method);
+ }
+ }
+
+ static void AddFunctionsForScript(GenericMenu menu, SerializedProperty listener, ValidMethodMap method, string targetName)
+ {
+ PersistentListenerMode mode = method.mode;
+
+ // find the current event target...
+ var listenerTarget = listener.FindPropertyRelative(kInstancePath).objectReferenceValue;
+ var methodName = listener.FindPropertyRelative(kMethodNamePath).stringValue;
+ var setMode = GetMode(listener.FindPropertyRelative(kModePath));
+ var typeName = listener.FindPropertyRelative(kArgumentsPath).FindPropertyRelative(kObjectArgumentAssemblyTypeName);
+
+ var args = new StringBuilder();
+ var count = method.methodInfo.GetParameters().Length;
+ for (int index = 0; index < count; index++)
+ {
+ var methodArg = method.methodInfo.GetParameters()[index];
+ args.Append(string.Format("{0}", GetTypeName(methodArg.ParameterType)));
+
+ if (index < count - 1)
+ args.Append(", ");
+ }
+
+ var isCurrentlySet = listenerTarget == method.target
+ && methodName == method.methodInfo.Name
+ && mode == setMode;
+
+ if (isCurrentlySet && mode == PersistentListenerMode.Object && method.methodInfo.GetParameters().Length == 1)
+ {
+ isCurrentlySet &= (method.methodInfo.GetParameters()[0].ParameterType.AssemblyQualifiedName == typeName.stringValue);
+ }
+
+ string path = GetFormattedMethodName(targetName, method.methodInfo.Name, args.ToString(), mode == PersistentListenerMode.EventDefined);
+ menu.AddItem(new GUIContent(path),
+ isCurrentlySet,
+ SetEventFunction,
+ new UnityEventFunction(listener, method.target, method.methodInfo, mode));
+ }
+
+ private static string GetTypeName(Type t)
+ {
+ if (t == typeof(int))
+ return "int";
+ if (t == typeof(float))
+ return "float";
+ if (t == typeof(string))
+ return "string";
+ if (t == typeof(bool))
+ return "bool";
+ return t.Name;
+ }
+
+ static string GetFormattedMethodName(string targetName, string methodName, string args, bool dynamic)
+ {
+ if (dynamic)
+ {
+ if (methodName.StartsWith("set_"))
+ return string.Format("{0}/{1}", targetName, methodName.Substring(4));
+ else
+ return string.Format("{0}/{1}", targetName, methodName);
+ }
+ else
+ {
+ if (methodName.StartsWith("set_"))
+ return string.Format("{0}/{2} {1}", targetName, methodName.Substring(4), args);
+ else
+ return string.Format("{0}/{1} ({2})", targetName, methodName, args);
+ }
+ }
+
+ static void SetEventFunction(object source)
+ {
+ ((UnityEventFunction)source).Assign();
+ }
+
+ static void ClearEventFunction(object source)
+ {
+ ((UnityEventFunction)source).Clear();
+ }
+
+ struct UnityEventFunction
+ {
+ readonly SerializedProperty m_Listener;
+ readonly Object m_Target;
+ readonly MethodInfo m_Method;
+ readonly PersistentListenerMode m_Mode;
+
+ public UnityEventFunction(SerializedProperty listener, Object target, MethodInfo method, PersistentListenerMode mode)
+ {
+ m_Listener = listener;
+ m_Target = target;
+ m_Method = method;
+ m_Mode = mode;
+ }
+
+ public void Assign()
+ {
+ // find the current event target...
+ var listenerTarget = m_Listener.FindPropertyRelative(kInstancePath);
+ var methodName = m_Listener.FindPropertyRelative(kMethodNamePath);
+ var mode = m_Listener.FindPropertyRelative(kModePath);
+ var arguments = m_Listener.FindPropertyRelative(kArgumentsPath);
+
+ listenerTarget.objectReferenceValue = m_Target;
+ methodName.stringValue = m_Method.Name;
+ mode.enumValueIndex = (int)m_Mode;
+
+ if (m_Mode == PersistentListenerMode.Object)
+ {
+ var fullArgumentType = arguments.FindPropertyRelative(kObjectArgumentAssemblyTypeName);
+ var argParams = m_Method.GetParameters();
+ if (argParams.Length == 1 && typeof(Object).IsAssignableFrom(argParams[0].ParameterType))
+ fullArgumentType.stringValue = argParams[0].ParameterType.AssemblyQualifiedName;
+ else
+ fullArgumentType.stringValue = typeof(Object).AssemblyQualifiedName;
+ }
+
+ ValidateObjectParamater(arguments, m_Mode);
+
+ m_Listener.serializedObject.ApplyModifiedProperties();
+ //HENRY m_Listener.m_SerializedObject.ApplyModifiedProperties();
+ }
+
+ private void ValidateObjectParamater(SerializedProperty arguments, PersistentListenerMode mode)
+ {
+ var fullArgumentType = arguments.FindPropertyRelative(kObjectArgumentAssemblyTypeName);
+ var argument = arguments.FindPropertyRelative(kObjectArgument);
+ var argumentObj = argument.objectReferenceValue;
+
+ if (mode != PersistentListenerMode.Object)
+ {
+ fullArgumentType.stringValue = typeof(Object).AssemblyQualifiedName;
+ argument.objectReferenceValue = null;
+ return;
+ }
+
+ if (argumentObj == null)
+ return;
+
+ Type t = Type.GetType(fullArgumentType.stringValue, false);
+ if (!typeof(Object).IsAssignableFrom(t) || !t.IsInstanceOfType(argumentObj))
+ argument.objectReferenceValue = null;
+ }
+
+ public void Clear()
+ {
+ // find the current event target...
+ var methodName = m_Listener.FindPropertyRelative(kMethodNamePath);
+ methodName.stringValue = null;
+
+ var mode = m_Listener.FindPropertyRelative(kModePath);
+ mode.enumValueIndex = (int)PersistentListenerMode.Void;
+
+ //HENRY: m_Listener.m_SerializedObject.ApplyModifiedProperties();
+ m_Listener.serializedObject.ApplyModifiedProperties();
+ }
+ }
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Scripts/Compatibility/Editor/UnityEventDrawer.cs.meta b/Scripts/Compatibility/Editor/UnityEventDrawer.cs.meta
new file mode 100644
index 00000000..52a4397b
--- /dev/null
+++ b/Scripts/Compatibility/Editor/UnityEventDrawer.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 36a674334511c7b4c8d978720cf5b485
+timeCreated: 1528787041
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Constants.cs b/Scripts/Constants.cs
new file mode 100644
index 00000000..9fb5f60a
--- /dev/null
+++ b/Scripts/Constants.cs
@@ -0,0 +1,18 @@
+#if UNITY_EDITOR || RUNTIME_CSG
+using System;
+
+namespace Sabresaurus.SabreCSG
+{
+ ///
+ /// Provides commonly used string constants.
+ ///
+ internal static class Constants
+ {
+ ///
+ /// The game object volume component identifier.
+ /// This is used for the hidden built volume game objects for volume brushes.
+ ///
+ public const string GameObjectVolumeComponentIdentifier = "SabreCSG: Volume Component (67173f4f-868c-4c70-ae40-335550c8354f)";
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Scripts/Constants.cs.meta b/Scripts/Constants.cs.meta
new file mode 100644
index 00000000..647d106b
--- /dev/null
+++ b/Scripts/Constants.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: b29d9ecccfbc8fb4f8885d4db46b3b9b
+timeCreated: 1528659610
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Core/BrushBase.cs b/Scripts/Core/BrushBase.cs
index 8b2cfb55..66f25df5 100644
--- a/Scripts/Core/BrushBase.cs
+++ b/Scripts/Core/BrushBase.cs
@@ -9,7 +9,13 @@
namespace Sabresaurus.SabreCSG
{
- public enum CSGMode { Add, Subtract };
+ public enum CSGMode
+ {
+ Add,
+ Subtract,
+ Volume
+ };
+
[ExecuteInEditMode]
public abstract class BrushBase : MonoBehaviour
{
@@ -25,7 +31,10 @@ public abstract class BrushBase : MonoBehaviour
[SerializeField]
protected bool isVisible = true;
- protected bool destroyed = false;
+ [SerializeField]
+ protected Volume volume = null;
+
+ protected bool destroyed = false;
protected string previousHierarchyName = "";
@@ -39,6 +48,21 @@ public CSGMode Mode
{
if (this.mode != value)
{
+ if (value == CSGMode.Volume)
+ {
+ // limitation: volume mode can only be set on primitive brushes for now.
+ if (this.GetType() != typeof(PrimitiveBrush))
+ {
+ return;
+ }
+ // limitation: prevent users from setting compound brush primitives to volumes.
+ if (((PrimitiveBrush)this).BrushController != null)
+ {
+ return;
+ }
+ // we prevent this so that we can add our own unique compound brush workflow in later SabreCSG versions.
+ }
+
this.mode = value;
Invalidate(true);
@@ -90,6 +114,22 @@ public bool HasCollision
}
}
+ ///
+ /// Gets or sets the volume associated with the brush (for ).
+ ///
+ /// The volume.
+ public Volume Volume
+ {
+ get
+ {
+ return volume;
+ }
+ set
+ {
+ volume = value;
+ }
+ }
+
///
/// Gets the beautiful name of the brush used in auto-generation of the hierarchy name.
///
@@ -130,6 +170,67 @@ public void UpdateGeneratedHierarchyName()
transform.name = previousHierarchyName = GeneratedHierarchyName;
}
+ ///
+ /// Rebuilds the volume, creating or deleting the volume component and applying new settings.
+ ///
+ internal void RebuildVolume()
+ {
+ // volumes can only be primitive brushes at the moment.
+ if (GetType() != typeof(PrimitiveBrush)) return;
+ PrimitiveBrush self = (PrimitiveBrush)this;
+
+ // remove volumes from brushes that are no longer volumes:
+ if (Mode != CSGMode.Volume && Volume != null)
+ {
+ // set volume handle to null.
+ Volume = null;
+ // delete any built volume.
+ Transform volume1 = transform.Find(Constants.GameObjectVolumeComponentIdentifier);
+ if (volume1 != null)
+ GameObject.DestroyImmediate(volume1.gameObject);
+ }
+
+ // generate all of the volume brushes:
+ if (Mode == CSGMode.Volume && Volume != null)
+ {
+ // remove any existing built volume:
+ Transform volume2 = transform.Find(Constants.GameObjectVolumeComponentIdentifier);
+ if (volume2 != null)
+ GameObject.DestroyImmediate(volume2.gameObject);
+
+ // create the game object with convex mesh collider:
+ Mesh mesh = new Mesh();
+ BrushFactory.GenerateMeshFromPolygonsFast(self.GetPolygons(), ref mesh, 0.0f);
+ GameObject gameObject = CreateVolumeMeshCollider(mesh);
+ gameObject.transform.position = transform.position;
+ gameObject.transform.rotation = transform.rotation;
+
+ // execute custom volume generation code:
+ Volume.OnCreateVolume(gameObject);
+ }
+ }
+
+ ///
+ /// Creates the game object for the volume brush including a mesh collider.
+ ///
+ /// The mesh to be used for the mesh collider.
+ /// The created game object.
+ internal GameObject CreateVolumeMeshCollider(Mesh mesh)
+ {
+ GameObject volumeMesh = new GameObject(Constants.GameObjectVolumeComponentIdentifier, typeof(MeshCollider));
+ volumeMesh.transform.SetParent(transform, false);
+#if UNITY_EDITOR
+ if (!CurrentSettings.ShowHiddenGameObjectsInHierarchy)
+ volumeMesh.hideFlags = HideFlags.HideInHierarchy | HideFlags.NotEditable;
+#endif
+ // Set the mesh to be used for triggers.
+ MeshCollider meshCollider = volumeMesh.GetComponent();
+ meshCollider.sharedMesh = mesh;
+ meshCollider.convex = true;
+ meshCollider.isTrigger = true;
+ return volumeMesh;
+ }
+
///
/// Gets a value indicating whether this brush supports CSG operations. Setting this to false
/// will hide CSG brush related options in the editor.
@@ -140,9 +241,15 @@ public void UpdateGeneratedHierarchyName()
public virtual void Invalidate(bool polygonsChanged)
{
- // when a modification to a brush occured we update the auto-generated name.
+ // when a modification to a brush occured:
if (polygonsChanged)
+ {
+ // we update the auto-generated name.
UpdateGeneratedHierarchyName();
+
+ // we rebuild the volumes.
+ RebuildVolume();
+ }
}
public abstract void UpdateVisibility();
diff --git a/Scripts/Core/BuildEngine/BrushBuilder.cs b/Scripts/Core/BuildEngine/BrushBuilder.cs
index f03aa1ee..792950f9 100644
--- a/Scripts/Core/BuildEngine/BrushBuilder.cs
+++ b/Scripts/Core/BuildEngine/BrushBuilder.cs
@@ -74,8 +74,8 @@ internal static void Build(BrushCache brushCache, int brushIndex, BrushCache[] a
// Remove any polygons that are contained in an earlier addition
RemoveInteriorPolygons(brushChunks, intersectingBrushCaches[i], excludedPolygons, polygonsRemoved);
}
- else
- {
+ else if (intersectingBrushCaches[i].Mode == CSGMode.Subtract)
+ {
// Restore any polygons that were removed when inside an addition but are now inside a subsequent subtraction
RestoreInteriorPolygons(brushChunks, intersectingBrushCaches[i], excludedPolygons, polygonsRemoved);
}
@@ -87,8 +87,8 @@ internal static void Build(BrushCache brushCache, int brushIndex, BrushCache[] a
// Remove any polygons that will be contrained by a later additive brush
RemoveInteriorPolygons(brushChunks, intersectingBrushCaches[i], excludedPolygons, polygonsRemoved);
}
- else
- {
+ else if (intersectingBrushCaches[i].Mode == CSGMode.Subtract)
+ {
// If the later brush is subtractive, extract any subtractive polygons and add to these chunks
ExtractSubtractionPolygons(brushCache, brushChunks, intersectingBrushCaches[i], isCollisionPass);
}
@@ -151,8 +151,8 @@ internal static void Build(BrushCache brushCache, int brushIndex, BrushCache[] a
// DebugExclude.DisplayChunk(DebugExclude.hackyHolder, brushChunks[i], brushIndex);
// }
}
- else // Subtract
- {
+ else if (brushCache.Mode == CSGMode.Subtract)
+ {
// Do nothing for subtractive brushes. This is handled by the additive brushes they interact with
if(isCollisionPass)
{
diff --git a/Scripts/Core/BuildEngine/CSGFactory.cs b/Scripts/Core/BuildEngine/CSGFactory.cs
index db82a23b..bff5841c 100644
--- a/Scripts/Core/BuildEngine/CSGFactory.cs
+++ b/Scripts/Core/BuildEngine/CSGFactory.cs
@@ -265,7 +265,7 @@ internal static void CoreBuild(object state)
// Intersecting builders can probably be calculated at edit time
BrushBuilder.Build(allBrushCaches[brushIndex], brushIndex, allBrushCaches, false);
- brushesBuilt++;
+ brushesBuilt++;
// If we are not required to build collision (either for this brush, or at all) then we've built it!
if(!shouldBuildCollision[brushIndex] || !buildSettings.GenerateCollisionMeshes)
@@ -476,8 +476,8 @@ internal static bool FinalizeBuild()
MeshGroupManager.BuildCollision(meshGroupHolder, buildContext.CollisionPolygonIndex, buildSettings, collisionMeshDictionary);
}
- // All done
- DateTime time2 = DateTime.Now;
+ // All done
+ DateTime time2 = DateTime.Now;
buildContext.buildMetrics.BuildMetaData = (time1-buildStartTime).TotalSeconds + " " + (time2-time1).TotalSeconds + " " + brushesBuilt;
buildContext.buildMetrics.BuildTime = (float)(DateTime.Now - buildStartTime).TotalSeconds;
@@ -546,6 +546,6 @@ public static GameObject CreateCollisionMesh(Transform rootTransform, Mesh mesh)
return colliderMesh;
}
- }
+ }
}
#endif
\ No newline at end of file
diff --git a/Scripts/Core/BuildEngine/MeshGroupManager.cs b/Scripts/Core/BuildEngine/MeshGroupManager.cs
index 8c71974d..ee2ae1fc 100755
--- a/Scripts/Core/BuildEngine/MeshGroupManager.cs
+++ b/Scripts/Core/BuildEngine/MeshGroupManager.cs
@@ -1,4 +1,5 @@
#if UNITY_EDITOR || RUNTIME_CSG
+
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
@@ -6,463 +7,463 @@
using System.Reflection;
using UnityEngine.SceneManagement;
using System.IO;
+using UnityEngine.Rendering;
namespace Sabresaurus.SabreCSG
{
- internal static class MeshGroupManager
- {
- internal static Action OnFinalizeVisualMesh = null;
- internal static Action OnFinalizeCollisionMesh = null;
+ internal static class MeshGroupManager
+ {
+ internal static Action OnFinalizeVisualMesh = null;
+ internal static Action OnFinalizeCollisionMesh = null;
- private const int MESH_VERTEX_LIMIT = 65500;
+ private const int MESH_VERTEX_LIMIT = 65500;
- internal static void Cleanup(Transform meshGroupHolder)
- {
- // Destroy all the old meshes to prevent them leaking
- MeshFilter[] filters = meshGroupHolder.GetComponentsInChildren();
- MeshCollider[] colliders = meshGroupHolder.GetComponentsInChildren();
+ internal static void Cleanup(Transform meshGroupHolder)
+ {
+ // Destroy all the old meshes to prevent them leaking
+ MeshFilter[] filters = meshGroupHolder.GetComponentsInChildren();
+ MeshCollider[] colliders = meshGroupHolder.GetComponentsInChildren();
- for (int i = 0; i < filters.Length; i++)
- {
+ for (int i = 0; i < filters.Length; i++)
+ {
#if UNITY_EDITOR
- if(filters[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(filters[i].sharedMesh))
-#endif
- GameObject.DestroyImmediate(filters[i].sharedMesh);
- }
+ if (filters[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(filters[i].sharedMesh))
+#endif
+ GameObject.DestroyImmediate(filters[i].sharedMesh);
+ }
- for (int i = 0; i < colliders.Length; i++)
- {
+ for (int i = 0; i < colliders.Length; i++)
+ {
#if UNITY_EDITOR
- if(colliders[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(colliders[i].sharedMesh))
-#endif
- GameObject.DestroyImmediate(colliders[i].sharedMesh);
- }
-
- // Finally destroy the game objects and components
- meshGroupHolder.DestroyChildrenImmediate();
- }
-
- internal static void BuildVisual(Transform meshGroupHolder,
- PolygonEntry[] polygonIndex,
- CSGBuildSettings buildSettings,
- CSGBuildContext.BuildContext buildContext,
- MaterialMeshDictionary materialMeshDictionary)
- {
- materialMeshDictionary.Clear();
-
- // Reset statistics
- buildContext.buildMetrics.TotalMeshes = 0;
- buildContext.buildMetrics.TotalVertices = 0;
- buildContext.buildMetrics.TotalTriangles = 0;
-
- Dictionary> polygonMaterialTable = new Dictionary>();
-
- for (int i = 0; i < polygonIndex.Length; i++)
- {
- PolygonEntry entry = polygonIndex[i];
-
- if(PolygonEntry.IsValid(entry)
- && entry.Positions.Length > 0
- && !entry.ExcludeFromBuild) // Skip polygons that weren't built
- {
- Material material = entry.Material;
-
- if(material == null)
- {
- material = buildSettings.DefaultVisualMaterial;
- }
-
- if(polygonMaterialTable.ContainsKey(material))
- {
- polygonMaterialTable[material].Add(entry);
- }
- else
- {
- polygonMaterialTable.Add(material, new List() { entry } );
- }
- }
- }
-
- foreach (KeyValuePair> row in polygonMaterialTable)
- {
- Mesh mesh = new Mesh();
-
- List positionsList = new List();
- List normalsList = new List();
- List uvList = new List();
- List colorsList = new List();
- List trianglesList = new List();
-
- for (int i = 0; i < row.Value.Count; i++)
- {
- int positionOffset = positionsList.Count;
- int triangleOffset = trianglesList.Count;
-
- PolygonEntry polygonEntry = row.Value[i];
- if(polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT)
- {
- FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary);
- mesh = new Mesh();
- positionsList.Clear();
- normalsList.Clear();
- uvList.Clear();
- colorsList.Clear();
- trianglesList.Clear();
- positionOffset = 0;
- }
- positionsList.AddRange(polygonEntry.Positions);
- normalsList.AddRange(polygonEntry.Normals);
- uvList.AddRange(polygonEntry.UV);
- colorsList.AddRange(polygonEntry.Colors);
-
- for (int j = 0; j < polygonEntry.Triangles.Length; j++)
- {
- trianglesList.Add(polygonEntry.Triangles[j] + positionOffset);
- }
-
- row.Value[i].BuiltMesh = mesh;
- row.Value[i].BuiltVertexOffset = positionOffset;
- row.Value[i].BuiltTriangleOffset = triangleOffset;
- }
-
- FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary);
- }
- }
-
- internal static void FinalizeVisualMesh(Transform meshGroupHolder,
- Mesh mesh,
- Material material,
- CSGBuildSettings buildSettings,
- CSGBuildContext.BuildContext buildContext,
- List positionsList,
- List normalsList,
- List uvList,
- List colorsList,
- List trianglesList,
- MaterialMeshDictionary materialMeshDictionary)
- {
- Vector3[] positionsArray = new Vector3[positionsList.Count];
- Vector3[] normalsArray = new Vector3[normalsList.Count];
- Vector2[] uvArray = new Vector2[uvList.Count];
- Color[] colorsArray = new Color[colorsList.Count];
- int[] trianglesArray = new int[trianglesList.Count];
-
- positionsList.CopyTo(positionsArray);
- normalsList.CopyTo(normalsArray);
- uvList.CopyTo(uvArray);
- trianglesList.CopyTo(trianglesArray);
- colorsList.CopyTo(colorsArray);
-
- mesh.vertices = positionsArray;
- mesh.normals = normalsArray;
- mesh.uv = uvArray;
- mesh.colors = colorsArray;
-
-
- if(meshGroupHolder.position != Vector3.zero
- || meshGroupHolder.rotation != Quaternion.identity
- || meshGroupHolder.lossyScale != Vector3.one)
- {
- mesh.LocalizeToTransform(meshGroupHolder);
- }
-
- mesh.triangles = trianglesArray;
-
- if (buildSettings.GenerateTangents)
- {
- // Generate tangents, necessary for some shaders
- mesh.GenerateTangents();
- }
-
- buildContext.buildMetrics.TotalMeshes++;
- buildContext.buildMetrics.TotalVertices += positionsArray.Length;
- buildContext.buildMetrics.TotalTriangles += trianglesArray.Length / 3;
-
- GameObject newGameObject = new GameObject("MaterialMesh");
-
- // Allow any editor dependent code to fire, e.g. lightmap unwrapping, static flags
- if(OnFinalizeVisualMesh != null)
- {
- OnFinalizeVisualMesh(newGameObject, mesh);
- }
-
-
- newGameObject.AddComponent().sharedMesh = mesh;
+ if (colliders[i].sharedMesh != null && !UnityEditor.AssetDatabase.Contains(colliders[i].sharedMesh))
+#endif
+ GameObject.DestroyImmediate(colliders[i].sharedMesh);
+ }
+
+ // Finally destroy the game objects and components
+ meshGroupHolder.DestroyChildrenImmediate();
+ }
+
+ internal static void BuildVisual(Transform meshGroupHolder,
+ PolygonEntry[] polygonIndex,
+ CSGBuildSettings buildSettings,
+ CSGBuildContext.BuildContext buildContext,
+ MaterialMeshDictionary materialMeshDictionary)
+ {
+ materialMeshDictionary.Clear();
+
+ // Reset statistics
+ buildContext.buildMetrics.TotalMeshes = 0;
+ buildContext.buildMetrics.TotalVertices = 0;
+ buildContext.buildMetrics.TotalTriangles = 0;
+
+ Dictionary> polygonMaterialTable = new Dictionary>();
+
+ for (int i = 0; i < polygonIndex.Length; i++)
+ {
+ PolygonEntry entry = polygonIndex[i];
+
+ if (PolygonEntry.IsValid(entry)
+ && entry.Positions.Length > 0
+ && !entry.ExcludeFromBuild) // Skip polygons that weren't built
+ {
+ Material material = entry.Material;
+
+ if (material == null)
+ {
+ material = buildSettings.DefaultVisualMaterial;
+ }
+
+ if (polygonMaterialTable.ContainsKey(material))
+ {
+ polygonMaterialTable[material].Add(entry);
+ }
+ else
+ {
+ polygonMaterialTable.Add(material, new List() { entry });
+ }
+ }
+ }
+
+ foreach (KeyValuePair> row in polygonMaterialTable)
+ {
+ Mesh mesh = new Mesh();
+
+ List positionsList = new List();
+ List normalsList = new List();
+ List uvList = new List();
+ List colorsList = new List();
+ List trianglesList = new List();
+
+ for (int i = 0; i < row.Value.Count; i++)
+ {
+ int positionOffset = positionsList.Count;
+ int triangleOffset = trianglesList.Count;
+
+ PolygonEntry polygonEntry = row.Value[i];
+ if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT)
+ {
+ FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary);
+ mesh = new Mesh();
+ positionsList.Clear();
+ normalsList.Clear();
+ uvList.Clear();
+ colorsList.Clear();
+ trianglesList.Clear();
+ positionOffset = 0;
+ }
+ positionsList.AddRange(polygonEntry.Positions);
+ normalsList.AddRange(polygonEntry.Normals);
+ uvList.AddRange(polygonEntry.UV);
+ colorsList.AddRange(polygonEntry.Colors);
+
+ for (int j = 0; j < polygonEntry.Triangles.Length; j++)
+ {
+ trianglesList.Add(polygonEntry.Triangles[j] + positionOffset);
+ }
+
+ row.Value[i].BuiltMesh = mesh;
+ row.Value[i].BuiltVertexOffset = positionOffset;
+ row.Value[i].BuiltTriangleOffset = triangleOffset;
+ }
+
+ FinalizeVisualMesh(meshGroupHolder, mesh, row.Key, buildSettings, buildContext, positionsList, normalsList, uvList, colorsList, trianglesList, materialMeshDictionary);
+ }
+ }
+
+ internal static void FinalizeVisualMesh(Transform meshGroupHolder,
+ Mesh mesh,
+ Material material,
+ CSGBuildSettings buildSettings,
+ CSGBuildContext.BuildContext buildContext,
+ List positionsList,
+ List normalsList,
+ List uvList,
+ List colorsList,
+ List trianglesList,
+ MaterialMeshDictionary materialMeshDictionary)
+ {
+ Vector3[] positionsArray = new Vector3[positionsList.Count];
+ Vector3[] normalsArray = new Vector3[normalsList.Count];
+ Vector2[] uvArray = new Vector2[uvList.Count];
+ Color[] colorsArray = new Color[colorsList.Count];
+ int[] trianglesArray = new int[trianglesList.Count];
+
+ positionsList.CopyTo(positionsArray);
+ normalsList.CopyTo(normalsArray);
+ uvList.CopyTo(uvArray);
+ trianglesList.CopyTo(trianglesArray);
+ colorsList.CopyTo(colorsArray);
+
+ mesh.vertices = positionsArray;
+ mesh.normals = normalsArray;
+ mesh.uv = uvArray;
+ mesh.colors = colorsArray;
+
+ if (meshGroupHolder.position != Vector3.zero
+ || meshGroupHolder.rotation != Quaternion.identity
+ || meshGroupHolder.lossyScale != Vector3.one)
+ {
+ mesh.LocalizeToTransform(meshGroupHolder);
+ }
+
+ mesh.triangles = trianglesArray;
+
+ if (buildSettings.GenerateTangents)
+ {
+ // Generate tangents, necessary for some shaders
+ mesh.GenerateTangents();
+ }
+
+ buildContext.buildMetrics.TotalMeshes++;
+ buildContext.buildMetrics.TotalVertices += positionsArray.Length;
+ buildContext.buildMetrics.TotalTriangles += trianglesArray.Length / 3;
+
+ GameObject newGameObject = new GameObject("MaterialMesh");
+
+ // Allow any editor dependent code to fire, e.g. lightmap unwrapping, static flags
+ if (OnFinalizeVisualMesh != null)
+ {
+ OnFinalizeVisualMesh(newGameObject, mesh);
+ }
+
+ newGameObject.AddComponent().sharedMesh = mesh;
MeshRenderer meshRenderer = newGameObject.AddComponent();
meshRenderer.sharedMaterial = material;
meshRenderer.shadowCastingMode = buildSettings.ShadowCastingMode;
-// newGameObject.transform.parent = meshGroupHolder;
- newGameObject.transform.SetParent(meshGroupHolder, false);
+ meshRenderer.reflectionProbeUsage = buildSettings.ReflectionProbeUsage;
+ //newGameObject.transform.parent = meshGroupHolder;
+
+ newGameObject.transform.SetParent(meshGroupHolder, false);
#if UNITY_EDITOR
- if(buildSettings.SaveMeshesAsAssets)
+ if (buildSettings.SaveMeshesAsAssets)
{
// Make sure the folder exists to save into
string path = SceneManager.GetActiveScene().path;
path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
- if(!Directory.Exists(path))
+ if (!Directory.Exists(path))
{
- UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path));
+ UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path));
}
// Save to a file rather than leaving it as a scene asset
UnityEditor.AssetDatabase.CreateAsset(mesh, Path.Combine(path, "VisualMesh" + materialMeshDictionary.MeshCount + ".asset"));
}
#endif
- materialMeshDictionary.Add(material, mesh, newGameObject);
- }
-
- internal static void BuildCollision(Transform meshGroupHolder,
- PolygonEntry[] polygonIndex,
- CSGBuildSettings buildSettings,
- List collisionMeshDictionary)
- {
- collisionMeshDictionary.Clear();
-
- if(polygonIndex.Length > 0)
- {
- Mesh mesh = new Mesh();
- List positionsList = new List();
- List normalsList = new List();
- List uvList = new List();
- List trianglesList = new List();
-
- for (int i = 0; i < polygonIndex.Length; i++)
- {
- if(polygonIndex[i] != null)
- {
- int positionOffset = positionsList.Count;
- int triangleOffset = trianglesList.Count;
-
- PolygonEntry polygonEntry = polygonIndex[i];
-
- if(PolygonEntry.IsValid(polygonEntry)
- && polygonEntry.Positions.Length > 0
- && !polygonEntry.ExcludeFromBuild) // Skip polygons that weren't built
- {
- if(polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT)
- {
- FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary);
- mesh = new Mesh();
- positionsList.Clear();
- normalsList.Clear();
- uvList.Clear();
- trianglesList.Clear();
- positionOffset = 0;
- }
- positionsList.AddRange(polygonEntry.Positions);
- normalsList.AddRange(polygonEntry.Normals);
- uvList.AddRange(polygonEntry.UV);
-
- for (int j = 0; j < polygonEntry.Triangles.Length; j++)
- {
- trianglesList.Add(polygonEntry.Triangles[j] + positionOffset);
- }
-
- polygonEntry.BuiltMesh = mesh;
- polygonEntry.BuiltVertexOffset = positionOffset;
- polygonEntry.BuiltTriangleOffset = triangleOffset;
- }
- }
- }
- FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary);
- }
- }
-
- internal static void FinalizeCollisionMesh(Transform meshGroupHolder,
- Mesh mesh,
- CSGBuildSettings buildSettings,
- List positionsList,
- List normalsList,
- List uvList,
- List trianglesList,
- List collisionMeshDictionary)
- {
- Vector3[] positionsArray = new Vector3[positionsList.Count];
- Vector3[] normalsArray = new Vector3[normalsList.Count];
- Vector2[] uvArray = new Vector2[uvList.Count];
- int[] trianglesArray = new int[trianglesList.Count];
-
- positionsList.CopyTo(positionsArray);
- normalsList.CopyTo(normalsArray);
- uvList.CopyTo(uvArray);
- trianglesList.CopyTo(trianglesArray);
-
- mesh.vertices = positionsArray;
- mesh.normals = normalsArray;
- mesh.uv = uvArray;
-
-
- if(meshGroupHolder.position != Vector3.zero
- || meshGroupHolder.rotation != Quaternion.identity
- || meshGroupHolder.lossyScale != Vector3.one)
- {
- mesh.LocalizeToTransform(meshGroupHolder);
- }
-
- mesh.triangles = trianglesArray;
-
- GameObject newGameObject = new GameObject("CollisionMesh");
-
- // Allow any editor dependent code to fire, e.g. assigning physics materials
- if(OnFinalizeCollisionMesh != null)
- {
- OnFinalizeCollisionMesh(newGameObject, mesh);
- }
-
- MeshCollider meshCollider = newGameObject.AddComponent();
- meshCollider.sharedMesh = mesh;
- // Assign the physics material from build settings
- meshCollider.sharedMaterial = buildSettings.DefaultPhysicsMaterial;
- // Reparent
- newGameObject.transform.SetParent(meshGroupHolder, false);//.parent = meshGroupHolder;
+ materialMeshDictionary.Add(material, mesh, newGameObject);
+ }
+
+ internal static void BuildCollision(Transform meshGroupHolder,
+ PolygonEntry[] polygonIndex,
+ CSGBuildSettings buildSettings,
+ List collisionMeshDictionary)
+ {
+ collisionMeshDictionary.Clear();
+
+ if (polygonIndex.Length > 0)
+ {
+ Mesh mesh = new Mesh();
+ List positionsList = new List();
+ List normalsList = new List();
+ List uvList = new List();
+ List trianglesList = new List();
+
+ for (int i = 0; i < polygonIndex.Length; i++)
+ {
+ if (polygonIndex[i] != null)
+ {
+ int positionOffset = positionsList.Count;
+ int triangleOffset = trianglesList.Count;
+
+ PolygonEntry polygonEntry = polygonIndex[i];
+
+ if (PolygonEntry.IsValid(polygonEntry)
+ && polygonEntry.Positions.Length > 0
+ && !polygonEntry.ExcludeFromBuild) // Skip polygons that weren't built
+ {
+ if (polygonEntry.Positions.Length + positionOffset > MESH_VERTEX_LIMIT)
+ {
+ FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary);
+ mesh = new Mesh();
+ positionsList.Clear();
+ normalsList.Clear();
+ uvList.Clear();
+ trianglesList.Clear();
+ positionOffset = 0;
+ }
+ positionsList.AddRange(polygonEntry.Positions);
+ normalsList.AddRange(polygonEntry.Normals);
+ uvList.AddRange(polygonEntry.UV);
+
+ for (int j = 0; j < polygonEntry.Triangles.Length; j++)
+ {
+ trianglesList.Add(polygonEntry.Triangles[j] + positionOffset);
+ }
+
+ polygonEntry.BuiltMesh = mesh;
+ polygonEntry.BuiltVertexOffset = positionOffset;
+ polygonEntry.BuiltTriangleOffset = triangleOffset;
+ }
+ }
+ }
+ FinalizeCollisionMesh(meshGroupHolder, mesh, buildSettings, positionsList, normalsList, uvList, trianglesList, collisionMeshDictionary);
+ }
+ }
+
+ internal static void FinalizeCollisionMesh(Transform meshGroupHolder,
+ Mesh mesh,
+ CSGBuildSettings buildSettings,
+ List positionsList,
+ List normalsList,
+ List uvList,
+ List trianglesList,
+ List collisionMeshDictionary)
+ {
+ Vector3[] positionsArray = new Vector3[positionsList.Count];
+ Vector3[] normalsArray = new Vector3[normalsList.Count];
+ Vector2[] uvArray = new Vector2[uvList.Count];
+ int[] trianglesArray = new int[trianglesList.Count];
+
+ positionsList.CopyTo(positionsArray);
+ normalsList.CopyTo(normalsArray);
+ uvList.CopyTo(uvArray);
+ trianglesList.CopyTo(trianglesArray);
+
+ mesh.vertices = positionsArray;
+ mesh.normals = normalsArray;
+ mesh.uv = uvArray;
+
+ if (meshGroupHolder.position != Vector3.zero
+ || meshGroupHolder.rotation != Quaternion.identity
+ || meshGroupHolder.lossyScale != Vector3.one)
+ {
+ mesh.LocalizeToTransform(meshGroupHolder);
+ }
+
+ mesh.triangles = trianglesArray;
+
+ GameObject newGameObject = new GameObject("CollisionMesh");
+
+ // Allow any editor dependent code to fire, e.g. assigning physics materials
+ if (OnFinalizeCollisionMesh != null)
+ {
+ OnFinalizeCollisionMesh(newGameObject, mesh);
+ }
+ MeshCollider meshCollider = newGameObject.AddComponent();
+ meshCollider.sharedMesh = mesh;
+ // Assign the physics material from build settings
+ meshCollider.sharedMaterial = buildSettings.DefaultPhysicsMaterial;
+ // Reparent
+ newGameObject.transform.SetParent(meshGroupHolder, false);//.parent = meshGroupHolder;
#if UNITY_EDITOR
- if(buildSettings.SaveMeshesAsAssets)
+ if (buildSettings.SaveMeshesAsAssets)
{
// Make sure the folder exists to save into
string path = SceneManager.GetActiveScene().path;
path = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path));
- if(!Directory.Exists(path))
+ if (!Directory.Exists(path))
{
- UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path));
+ UnityEditor.AssetDatabase.CreateFolder(Path.GetDirectoryName(path), Path.GetFileName(path));
}
// Save to a file rather than leaving it as a scene asset
UnityEditor.AssetDatabase.CreateAsset(mesh, Path.Combine(path, "CollisionMesh" + collisionMeshDictionary.Count + ".asset"));
}
#endif
- collisionMeshDictionary.Add(mesh);
- }
-
- internal static void TriangulateNewPolygons(bool individualVertices, Dictionary> groupedPolygons, PolygonEntry[] polygonIndex)
- {
- foreach (KeyValuePair> row in groupedPolygons)
- {
- Vector3[] newPositions;
- Vector3[] newNormals;
- Vector2[] newUV;
- Color[] newColors;
- int[] newTriangles;
-
- List polygons = row.Value;
-
- TriangulatePolygons(individualVertices, polygons, out newTriangles, out newPositions, out newNormals, out newUV, out newColors);
-
- polygonIndex[row.Key] = new PolygonEntry(newPositions, newNormals, newUV, newColors, newTriangles, polygons[0].Material, polygons[0].UserExcludeFromFinal);
- }
- }
-
- internal static void TriangulatePolygons(bool individualVertices, List polygons, out int[] triangesToAppend, out Vector3[] positions, out Vector3[] normals, out Vector2[] uv, out Color[] colors)
- {
- if(individualVertices)
- {
- int totalTriangleCount = 0;
- for (int i = 0; i < polygons.Count; i++)
- {
- totalTriangleCount += polygons[i].Vertices.Length - 2;
- }
- int totalVertexCount = totalTriangleCount * 3;
-
- positions = new Vector3[totalVertexCount];
- normals = new Vector3[totalVertexCount];
- uv = new Vector2[totalVertexCount];
- colors = new Color[totalVertexCount];
-
- triangesToAppend = new int[totalTriangleCount * 3];
-
- int triangleOffset = 0;
- int vertexOffset = 0;
-
- // Calculate triangulation
- for (int i = 0; i < polygons.Count; i++)
- {
- Polygon polygon = polygons[i];
- int triangleCount = polygons[i].Vertices.Length - 2;
-
- for (int j = 0; j < triangleCount; j++)
- {
- int sourceIndex = 0;
-
- positions[vertexOffset + j*3] = polygon.Vertices[sourceIndex].Position;
- normals[vertexOffset + j*3] = polygon.Vertices[sourceIndex].Normal;
- uv[vertexOffset + j*3] = polygon.Vertices[sourceIndex].UV;
- colors[vertexOffset + j*3] = polygon.Vertices[sourceIndex].Color;
-
- sourceIndex = j + 1;
-
- positions[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].Position;
- normals[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].Normal;
- uv[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].UV;
- colors[vertexOffset + j*3 + 1] = polygon.Vertices[sourceIndex].Color;
-
- sourceIndex = j + 2;
-
- positions[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].Position;
- normals[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].Normal;
- uv[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].UV;
- colors[vertexOffset + j*3 + 2] = polygon.Vertices[sourceIndex].Color;
- }
-
- for (int j = 0; j < triangleCount; j++)
- {
- triangesToAppend[triangleOffset + 0] = triangleOffset + 0;
- triangesToAppend[triangleOffset + 1] = triangleOffset + 1;
- triangesToAppend[triangleOffset + 2] = triangleOffset + 2;
-
- triangleOffset += 3;
- }
-
- vertexOffset += triangleCount * 3;
- }
- }
- else
- {
- int totalVertexCount = 0;
- for (int i = 0; i < polygons.Count; i++)
- {
- totalVertexCount += polygons[i].Vertices.Length;
- }
-
- int totalTriangleCount = totalVertexCount - 2 * polygons.Count;
-
- positions = new Vector3[totalVertexCount];
- normals = new Vector3[totalVertexCount];
- uv = new Vector2[totalVertexCount];
- colors = new Color[totalVertexCount];
-
- triangesToAppend = new int[totalTriangleCount * 3];
-
- int triangleOffset = 0;
- int vertexOffset = 0;
-
- // Calculate triangulation
- for (int i = 0; i < polygons.Count; i++)
- {
- Polygon polygon = polygons[i];
- int vertexCount = polygon.Vertices.Length;
-
- for (int j = 0; j < vertexCount; j++)
- {
- positions[vertexOffset + j] = polygon.Vertices[j].Position;
- normals[vertexOffset + j] = polygon.Vertices[j].Normal;
- uv[vertexOffset + j] = polygon.Vertices[j].UV;
- colors[vertexOffset + j] = polygon.Vertices[j].Color;
- }
-
- for (int j = 2; j < vertexCount; j++)
- {
- triangesToAppend[triangleOffset + 0] = vertexOffset + (0);
- triangesToAppend[triangleOffset + 1] = vertexOffset + (j - 1);
- triangesToAppend[triangleOffset + 2] = vertexOffset + (j);
- triangleOffset += 3;
- }
-
- vertexOffset += vertexCount;
- }
- }
- }
- }
+ collisionMeshDictionary.Add(mesh);
+ }
+
+ internal static void TriangulateNewPolygons(bool individualVertices, Dictionary> groupedPolygons, PolygonEntry[] polygonIndex)
+ {
+ foreach (KeyValuePair> row in groupedPolygons)
+ {
+ Vector3[] newPositions;
+ Vector3[] newNormals;
+ Vector2[] newUV;
+ Color[] newColors;
+ int[] newTriangles;
+
+ List polygons = row.Value;
+
+ TriangulatePolygons(individualVertices, polygons, out newTriangles, out newPositions, out newNormals, out newUV, out newColors);
+
+ polygonIndex[row.Key] = new PolygonEntry(newPositions, newNormals, newUV, newColors, newTriangles, polygons[0].Material, polygons[0].UserExcludeFromFinal);
+ }
+ }
+
+ internal static void TriangulatePolygons(bool individualVertices, List polygons, out int[] triangesToAppend, out Vector3[] positions, out Vector3[] normals, out Vector2[] uv, out Color[] colors)
+ {
+ if (individualVertices)
+ {
+ int totalTriangleCount = 0;
+ for (int i = 0; i < polygons.Count; i++)
+ {
+ totalTriangleCount += polygons[i].Vertices.Length - 2;
+ }
+ int totalVertexCount = totalTriangleCount * 3;
+
+ positions = new Vector3[totalVertexCount];
+ normals = new Vector3[totalVertexCount];
+ uv = new Vector2[totalVertexCount];
+ colors = new Color[totalVertexCount];
+
+ triangesToAppend = new int[totalTriangleCount * 3];
+
+ int triangleOffset = 0;
+ int vertexOffset = 0;
+
+ // Calculate triangulation
+ for (int i = 0; i < polygons.Count; i++)
+ {
+ Polygon polygon = polygons[i];
+ int triangleCount = polygons[i].Vertices.Length - 2;
+
+ for (int j = 0; j < triangleCount; j++)
+ {
+ int sourceIndex = 0;
+
+ positions[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].Position;
+ normals[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].Normal;
+ uv[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].UV;
+ colors[vertexOffset + j * 3] = polygon.Vertices[sourceIndex].Color;
+
+ sourceIndex = j + 1;
+
+ positions[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].Position;
+ normals[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].Normal;
+ uv[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].UV;
+ colors[vertexOffset + j * 3 + 1] = polygon.Vertices[sourceIndex].Color;
+
+ sourceIndex = j + 2;
+
+ positions[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].Position;
+ normals[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].Normal;
+ uv[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].UV;
+ colors[vertexOffset + j * 3 + 2] = polygon.Vertices[sourceIndex].Color;
+ }
+
+ for (int j = 0; j < triangleCount; j++)
+ {
+ triangesToAppend[triangleOffset + 0] = triangleOffset + 0;
+ triangesToAppend[triangleOffset + 1] = triangleOffset + 1;
+ triangesToAppend[triangleOffset + 2] = triangleOffset + 2;
+
+ triangleOffset += 3;
+ }
+
+ vertexOffset += triangleCount * 3;
+ }
+ }
+ else
+ {
+ int totalVertexCount = 0;
+ for (int i = 0; i < polygons.Count; i++)
+ {
+ totalVertexCount += polygons[i].Vertices.Length;
+ }
+
+ int totalTriangleCount = totalVertexCount - 2 * polygons.Count;
+
+ positions = new Vector3[totalVertexCount];
+ normals = new Vector3[totalVertexCount];
+ uv = new Vector2[totalVertexCount];
+ colors = new Color[totalVertexCount];
+
+ triangesToAppend = new int[totalTriangleCount * 3];
+
+ int triangleOffset = 0;
+ int vertexOffset = 0;
+
+ // Calculate triangulation
+ for (int i = 0; i < polygons.Count; i++)
+ {
+ Polygon polygon = polygons[i];
+ int vertexCount = polygon.Vertices.Length;
+
+ for (int j = 0; j < vertexCount; j++)
+ {
+ positions[vertexOffset + j] = polygon.Vertices[j].Position;
+ normals[vertexOffset + j] = polygon.Vertices[j].Normal;
+ uv[vertexOffset + j] = polygon.Vertices[j].UV;
+ colors[vertexOffset + j] = polygon.Vertices[j].Color;
+ }
+
+ for (int j = 2; j < vertexCount; j++)
+ {
+ triangesToAppend[triangleOffset + 0] = vertexOffset + (0);
+ triangesToAppend[triangleOffset + 1] = vertexOffset + (j - 1);
+ triangesToAppend[triangleOffset + 2] = vertexOffset + (j);
+ triangleOffset += 3;
+ }
+
+ vertexOffset += vertexCount;
+ }
+ }
+ }
+ }
}
+
#endif
\ No newline at end of file
diff --git a/Scripts/Core/CSG/Brush.cs b/Scripts/Core/CSG/Brush.cs
index ae97d27f..11d55d0a 100644
--- a/Scripts/Core/CSG/Brush.cs
+++ b/Scripts/Core/CSG/Brush.cs
@@ -101,12 +101,12 @@ public BrushCache BrushCache {
protected static List CalculateIntersectingBrushes(Brush sourceBrush, List brushes, bool isCollisionPass)
{
// If the brush is not CSG it can't intersect any brushes!
- if(sourceBrush.IsNoCSG)
+ if(sourceBrush.IsNoCSG || sourceBrush.Mode == CSGMode.Volume)
{
return new List();
}
- // Return empty lists if the pass is not relevant
- if(isCollisionPass)
+ // Return empty lists if the pass is not relevant
+ if (isCollisionPass)
{
if(!sourceBrush.hasCollision)
{
@@ -150,9 +150,9 @@ protected static List CalculateIntersectingBrushes(Brush sourceBrush, Lis
if(!Brush.IsInvalidForBuild(brushes[i]))
{
// Skip any brushes not suitable for the pass
- if(brushes[i].isNoCSG)
+ if(brushes[i].isNoCSG || brushes[i].mode == CSGMode.Volume)
{
- // NoCSG brushes skip the CSG calcs
+ // NoCSG and volume brushes skip the CSG calcs
continue;
}
else if(isCollisionPass && !brushes[i].HasCollision)
@@ -195,9 +195,9 @@ protected static List CalculateIntersectingBrushes(Brush sourceBrush, Lis
if(!Brush.IsInvalidForBuild(brushes[i]))
{
// Skip any brushes not suitable for the pass
- if(brushes[i].isNoCSG)
+ if(brushes[i].isNoCSG || brushes[i].mode == CSGMode.Volume)
{
- // NoCSG brushes skip the CSG calcs
+ // NoCSG and volume brushes skip the CSG calcs
continue;
}
else if(isCollisionPass && !brushes[i].HasCollision)
diff --git a/Scripts/Core/CSG/Polygon.cs b/Scripts/Core/CSG/Polygon.cs
index fec8e651..1ad243be 100644
--- a/Scripts/Core/CSG/Polygon.cs
+++ b/Scripts/Core/CSG/Polygon.cs
@@ -1,4 +1,5 @@
#if UNITY_EDITOR || RUNTIME_CSG
+
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
@@ -7,106 +8,118 @@
namespace Sabresaurus.SabreCSG
{
- [System.Serializable]
- public class Polygon : IDeepCopyable
- {
- [SerializeField]
- Vertex[] vertices;
-
- Plane? cachedPlane = null;
-
- // When a polygon is split or cloned, this number is preserved to those new objects so they can track where
- // they came from.
- [SerializeField]
- int uniqueIndex = -1;
-
- [SerializeField]
- Material material;
-
- // Set if the polygon is
- bool excludeFromFinal = false;
-
- [SerializeField]
- bool userExcludeFromFinal = false;
-
- public Vertex[] Vertices
- {
- get
- {
- return vertices;
- }
- set
- {
- vertices = value;
- CalculatePlane();
- }
- }
-
- public Plane CachedPlaneTest
- {
- get
- {
- return cachedPlane.Value;
- }
- }
-
- public Plane Plane
- {
- get
- {
- if(!cachedPlane.HasValue)
- {
- CalculatePlane();
- }
- return cachedPlane.Value;
- }
- }
-
- public Material Material {
- get {
- return material;
- }
- set {
- material = value;
- }
- }
-
- public int UniqueIndex {
- get {
- return uniqueIndex;
- }
- set {
- uniqueIndex = value;
- }
- }
-
- public bool ExcludeFromFinal {
- get {
- return excludeFromFinal;
- }
- set {
- excludeFromFinal = value;
- }
- }
-
- public bool UserExcludeFromFinal {
- get {
- return userExcludeFromFinal;
- }
- set {
- userExcludeFromFinal = value;
- }
- }
-
- public Polygon(Vertex[] vertices, Material material, bool isTemporary, bool userExcludeFromFinal, int uniqueIndex = -1)
- {
- this.vertices = vertices;
- this.material = material;
- this.uniqueIndex = uniqueIndex;
- this.excludeFromFinal = isTemporary;
- this.userExcludeFromFinal = userExcludeFromFinal;
- CalculatePlane();
- }
+ [System.Serializable]
+ public class Polygon : IDeepCopyable
+ {
+ [SerializeField]
+ private Vertex[] vertices;
+
+ private Plane? cachedPlane = null;
+
+ // When a polygon is split or cloned, this number is preserved to those new objects so they can track where
+ // they came from.
+ [SerializeField]
+ private int uniqueIndex = -1;
+
+ [SerializeField]
+ private Material material;
+
+ // Set if the polygon is
+ private bool excludeFromFinal = false;
+
+ [SerializeField]
+ private bool userExcludeFromFinal = false;
+
+ public Vertex[] Vertices
+ {
+ get
+ {
+ return vertices;
+ }
+ set
+ {
+ vertices = value;
+ CalculatePlane();
+ }
+ }
+
+ public Plane CachedPlaneTest
+ {
+ get
+ {
+ return cachedPlane.Value;
+ }
+ }
+
+ public Plane Plane
+ {
+ get
+ {
+ if (!cachedPlane.HasValue)
+ {
+ CalculatePlane();
+ }
+ return cachedPlane.Value;
+ }
+ }
+
+ public Material Material
+ {
+ get
+ {
+ return material;
+ }
+ set
+ {
+ material = value;
+ }
+ }
+
+ public int UniqueIndex
+ {
+ get
+ {
+ return uniqueIndex;
+ }
+ set
+ {
+ uniqueIndex = value;
+ }
+ }
+
+ public bool ExcludeFromFinal
+ {
+ get
+ {
+ return excludeFromFinal;
+ }
+ set
+ {
+ excludeFromFinal = value;
+ }
+ }
+
+ public bool UserExcludeFromFinal
+ {
+ get
+ {
+ return userExcludeFromFinal;
+ }
+ set
+ {
+ userExcludeFromFinal = value;
+ }
+ }
+
+ public Polygon(Vertex[] vertices, Material material, bool isTemporary, bool userExcludeFromFinal, int uniqueIndex = -1)
+ {
+ this.vertices = vertices;
+ this.material = material;
+ this.uniqueIndex = uniqueIndex;
+ this.excludeFromFinal = isTemporary;
+ this.userExcludeFromFinal = userExcludeFromFinal;
+ CalculatePlane();
+ }
///
/// Initializes a new instance of the class.
@@ -119,319 +132,322 @@ public Polygon(Vertex[] vertices, Material material, bool isTemporary, bool user
/// Thrown when is null.
/// A polygon must have at least 3 vertices.
public Polygon(Vector3[] vertices, Material material, bool isTemporary, bool userExcludeFromFinal, int uniqueIndex = -1)
- {
+ {
if (vertices == null) throw new ArgumentNullException("vertices");
if (vertices.Length < 3) throw new ArgumentOutOfRangeException("A polygon must have at least 3 vertices.");
// consideration: check array for null elements?
// create vertices from the vector3 array.
this.vertices = new Vertex[vertices.Length];
- for (int i = 0; i < vertices.Length; i++)
- this.vertices[i] = new Vertex(vertices[i], Vector3.zero, Vector2.zero);
+ for (int i = 0; i < vertices.Length; i++)
+ this.vertices[i] = new Vertex(vertices[i], Vector3.zero, Vector2.zero);
- this.material = material;
- this.uniqueIndex = uniqueIndex;
- this.excludeFromFinal = isTemporary;
- this.userExcludeFromFinal = userExcludeFromFinal;
+ this.material = material;
+ this.uniqueIndex = uniqueIndex;
+ this.excludeFromFinal = isTemporary;
+ this.userExcludeFromFinal = userExcludeFromFinal;
// calculate the cached plane.
CalculatePlane();
- }
-
- public Polygon DeepCopy()
- {
- return new Polygon(this.vertices.DeepCopy(), this.material, this.excludeFromFinal, this.userExcludeFromFinal, this.uniqueIndex);
- }
-
- public void CalculatePlane()
- {
- if(vertices.Length < 3)
- {
- return;
- }
- cachedPlane = new Plane(vertices[0].Position, vertices[1].Position, vertices[2].Position);
-
- // HACK: If the normal is zero and there's room to try another, then try alternate vertices
- if(cachedPlane.Value.normal == Vector3.zero && vertices.Length > 3)
- {
- int vertexIndex1 = 0;
- int vertexIndex2 = 1;
- int vertexIndex3 = 3;
-
- // Update UVs
- Vector3 pos1 = vertices[vertexIndex1].Position;
- Vector3 pos2 = vertices[vertexIndex2].Position;
- Vector3 pos3 = Vector3.zero;
-
- for (int i = 3; i < vertices.Length; i++)
- {
- vertexIndex3 = i;
-
- pos3 = vertices[vertexIndex3].Position;
-
- cachedPlane = new Plane(pos1,pos2,pos3);
-
- if(cachedPlane.Value.normal != Vector3.zero)
- {
- return;
- }
- }
-
-// if(cachedPlane.Value.normal == Vector3.zero)
-// {
-// Debug.LogError("Invalid Normal! Shouldn't be zero. Vertices count is " + vertices.Length);
-// }
- }
- }
-
- public void Flip()
- {
- // Reverse winding order
- System.Array.Reverse(this.vertices);
-
- // Flip each vertex normal
- for (int i = 0; i < this.vertices.Length; i++)
- {
- this.vertices[i].Normal *= -1;
- }
-
- // Flip the cached plane
+ }
+
+ public Polygon DeepCopy()
+ {
+ return new Polygon(this.vertices.DeepCopy(), this.material, this.excludeFromFinal, this.userExcludeFromFinal, this.uniqueIndex);
+ }
+
+ public void CalculatePlane()
+ {
+ if (vertices.Length < 3)
+ {
+ cachedPlane = new Plane();
+ return;
+ }
+ cachedPlane = new Plane(vertices[0].Position, vertices[1].Position, vertices[2].Position);
+
+ // HACK: If the normal is zero and there's room to try another, then try alternate vertices
+ if (cachedPlane.Value.normal == Vector3.zero && vertices.Length > 3)
+ {
+ int vertexIndex1 = 0;
+ int vertexIndex2 = 1;
+ int vertexIndex3 = 3;
+
+ // Update UVs
+ Vector3 pos1 = vertices[vertexIndex1].Position;
+ Vector3 pos2 = vertices[vertexIndex2].Position;
+ Vector3 pos3 = Vector3.zero;
+
+ for (int i = 3; i < vertices.Length; i++)
+ {
+ vertexIndex3 = i;
+
+ pos3 = vertices[vertexIndex3].Position;
+
+ cachedPlane = new Plane(pos1, pos2, pos3);
+
+ if (cachedPlane.Value.normal != Vector3.zero)
+ {
+ return;
+ }
+ }
+
+ // if(cachedPlane.Value.normal == Vector3.zero)
+ // {
+ // Debug.LogError("Invalid Normal! Shouldn't be zero. Vertices count is " + vertices.Length);
+ // }
+ }
+ }
+
+ public void Flip()
+ {
+ // Reverse winding order
+ System.Array.Reverse(this.vertices);
+
+ // Flip each vertex normal
+ for (int i = 0; i < this.vertices.Length; i++)
+ {
+ this.vertices[i].Normal *= -1;
+ }
+
+ // Flip the cached plane
#if UNITY_2017_1_OR_NEWER
// Unity 2017 introduces a built in Plane flipped property
- cachedPlane = cachedPlane.Value.flipped;
+ cachedPlane = cachedPlane.Value.flipped;
#else
cachedPlane = cachedPlane.Value.Flip();
#endif
- }
-
- public void SetVertices(Vertex[] vertices)
- {
- this.vertices = vertices;
- CalculatePlane();
- }
-
- public void ResetVertexNormals()
- {
- for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++)
- {
- vertices[vertexIndex].Normal = Plane.normal;
- }
- }
-
- public Bounds GetBounds()
- {
- if(vertices.Length > 0)
- {
- Bounds polygonBounds = new Bounds(vertices[0].Position, Vector3.zero);
-
- for (int j = 1; j < vertices.Length; j++)
- {
- polygonBounds.Encapsulate(vertices[j].Position);
- }
- return polygonBounds;
- }
- else
- {
- return new Bounds(Vector3.zero,Vector3.zero);
- }
- }
+ }
+
+ public void SetVertices(Vertex[] vertices)
+ {
+ this.vertices = vertices;
+ CalculatePlane();
+ }
+
+ public void ResetVertexNormals()
+ {
+ for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++)
+ {
+ vertices[vertexIndex].Normal = Plane.normal;
+ }
+ }
+
+ public Bounds GetBounds()
+ {
+ if (vertices.Length > 0)
+ {
+ Bounds polygonBounds = new Bounds(vertices[0].Position, Vector3.zero);
+
+ for (int j = 1; j < vertices.Length; j++)
+ {
+ polygonBounds.Encapsulate(vertices[j].Position);
+ }
+ return polygonBounds;
+ }
+ else
+ {
+ return new Bounds(Vector3.zero, Vector3.zero);
+ }
+ }
#if UNITY_EDITOR || RUNTIME_CSG
- public Edge[] GetEdges()
- {
- Edge[] edges = new Edge[vertices.Length];
-
- for (int vertexIndex1 = 0; vertexIndex1 < vertices.Length; vertexIndex1++)
- {
- // If our edge is from the last vertex it should be to the first vertex, otherwise to next vertex
- int vertexIndex2 = ((vertexIndex1 + 1) >= vertices.Length ? 0 : vertexIndex1 + 1);
- edges[vertexIndex1] = new Edge(vertices[vertexIndex1], vertices[vertexIndex2]);
- }
- return edges;
- }
+
+ public Edge[] GetEdges()
+ {
+ Edge[] edges = new Edge[vertices.Length];
+
+ for (int vertexIndex1 = 0; vertexIndex1 < vertices.Length; vertexIndex1++)
+ {
+ // If our edge is from the last vertex it should be to the first vertex, otherwise to next vertex
+ int vertexIndex2 = ((vertexIndex1 + 1) >= vertices.Length ? 0 : vertexIndex1 + 1);
+ edges[vertexIndex1] = new Edge(vertices[vertexIndex1], vertices[vertexIndex2]);
+ }
+ return edges;
+ }
+
#endif
- public Vector3 GetCenterPoint()
- {
- Vector3 center = vertices[0].Position;
- for (int i = 1; i < vertices.Length; i++)
- {
- center += vertices[i].Position;
- }
- center /= vertices.Length;
- return center;
- }
-
- public Vector3 GetCenterUV()
- {
- Vector2 centerUV = vertices[0].UV;
- for (int i = 1; i < vertices.Length; i++)
- {
- centerUV += vertices[i].UV;
- }
- centerUV *= 1f/vertices.Length;
- return centerUV;
- }
-
- public float GetArea()
- {
- Vector3 normal = Vector3.Normalize (Vector3.Cross (vertices[1].Position - vertices[0].Position, vertices[2].Position - vertices[0].Position));
- Quaternion cancellingRotation = Quaternion.Inverse(Quaternion.LookRotation(normal));
-
- float totalArea = 0;
-
- int j = vertices.Length-1;
- for (int i = 0; i < vertices.Length; i++)
- {
- Vector3 positionI = cancellingRotation * vertices[i].Position;
- Vector3 positionJ = cancellingRotation * vertices[j].Position;
- totalArea += (positionJ.x+positionI.x) * (positionJ.y-positionI.y);
-
- j=i;
- }
- return -totalArea * 0.5f;
- }
-
- public void SetColor(Color32 newColor)
- {
- for (int i = 0; i < vertices.Length; i++)
- {
- vertices[i].Color = newColor;
- }
- }
-
-
-
- public class Vector3ComparerEpsilon : IEqualityComparer
- {
- public bool Equals (Vector3 a, Vector3 b)
- {
- return Mathf.Abs(a.x - b.x) < EPSILON_LOWER
- && Mathf.Abs(a.y - b.y) < EPSILON_LOWER
- && Mathf.Abs(a.z - b.z) < EPSILON_LOWER;
- }
-
- public int GetHashCode (Vector3 obj)
- {
- // The similarity or difference between two positions can only be calculated if both are supplied
- // when Distinct is called GetHashCode is used to determine which values are in collision first
- // therefore we return the same hash code for all values to ensure all comparisons must use
- // our Equals method to properly determine which values are actually considered equal
- return 1;
- }
- }
-
- public class VertexComparerEpsilon : IEqualityComparer
- {
- public bool Equals (Vertex x, Vertex y)
- {
- return x.Position.EqualsWithEpsilon(y.Position);
- }
-
- public int GetHashCode (Vertex obj)
- {
- // The similarity or difference between two positions can only be calculated if both are supplied
- // when Distinct is called GetHashCode is used to determine which values are in collision first
- // therefore we return the same hash code for all values to ensure all comparisons must use
- // our Equals method to properly determine which values are actually considered equal
- return 1;
- }
- }
-
- public class PolygonUIDComparer : IEqualityComparer
- {
- public bool Equals (Polygon x, Polygon y)
- {
- return x.UniqueIndex == y.UniqueIndex;
- }
-
- public int GetHashCode (Polygon obj)
- {
- return base.GetHashCode();
- }
- }
-
-
- const float EPSILON = 0.00001f;
- const float EPSILON_LOWER = 0.001f;
-
-// const float EPSILON_LOWER = 0.003f;
-
- #region Static Methods
- public enum PolygonPlaneRelation { InFront, Behind, Spanning, Coplanar };
- public static PolygonPlaneRelation TestPolygonAgainstPlane(Polygon polygon, UnityEngine.Plane testPlane)
- {
- if (polygon.Plane.normal == testPlane.normal && polygon.Plane.distance == testPlane.distance)
- {
- return PolygonPlaneRelation.Coplanar;
- }
-
- // Count the number of vertices in front and behind the clip plane
- int verticesInFront = 0;
- int verticesBehind = 0;
-
- for (int i = 0; i < polygon.Vertices.Length; i++)
- {
- float distance = testPlane.GetDistanceToPoint(polygon.Vertices[i].Position);
- if (distance < -EPSILON_LOWER) // Is the point in front of the plane (with thickness)
- {
- verticesInFront++;
- }
- else if (distance > EPSILON_LOWER) // Is the point behind the plane (with thickness)
- {
- verticesBehind++;
- }
- }
-
- if (verticesInFront > 0 && verticesBehind > 0) // If some are in front and some are behind, then the poly spans
- {
- return PolygonPlaneRelation.Spanning;
- }
- else if (verticesInFront > 0) // Only in front, so entire polygon is in front
- {
- return PolygonPlaneRelation.InFront;
- }
- else if (verticesBehind > 0) // Only behind, so entire polygon is behind
- {
- return PolygonPlaneRelation.Behind;
- }
- else // No points in front or behind the plane, so assume coplanar
- {
- return PolygonPlaneRelation.Coplanar;
- }
- }
-
- // Loops through the vertices and removes any that share a position with any others so that
- // only uniquely positioned vertices remain
- public void RemoveExtraneousVertices()
- {
- List newVertices = new List();
- newVertices.Add(vertices[0]);
-
- for (int i = 1; i < vertices.Length; i++)
- {
- bool alreadyContained = false;
-
- for (int j = 0; j < newVertices.Count; j++) {
- if(vertices[i].Position.EqualsWithEpsilonLower(newVertices[j].Position))
- {
- alreadyContained = true;
- break;
- }
- }
-
- if(!alreadyContained)
- {
- newVertices.Add(vertices[i]);
- }
- }
-
- vertices = newVertices.ToArray();
- if(vertices.Length > 2)
- {
- CalculatePlane();
- }
- }
+ public Vector3 GetCenterPoint()
+ {
+ Vector3 center = vertices[0].Position;
+ for (int i = 1; i < vertices.Length; i++)
+ {
+ center += vertices[i].Position;
+ }
+ center /= vertices.Length;
+ return center;
+ }
+
+ public Vector3 GetCenterUV()
+ {
+ Vector2 centerUV = vertices[0].UV;
+ for (int i = 1; i < vertices.Length; i++)
+ {
+ centerUV += vertices[i].UV;
+ }
+ centerUV *= 1f / vertices.Length;
+ return centerUV;
+ }
+
+ public float GetArea()
+ {
+ Vector3 normal = Vector3.Normalize(Vector3.Cross(vertices[1].Position - vertices[0].Position, vertices[2].Position - vertices[0].Position));
+ Quaternion cancellingRotation = Quaternion.Inverse(Quaternion.LookRotation(normal));
+
+ float totalArea = 0;
+
+ int j = vertices.Length - 1;
+ for (int i = 0; i < vertices.Length; i++)
+ {
+ Vector3 positionI = cancellingRotation * vertices[i].Position;
+ Vector3 positionJ = cancellingRotation * vertices[j].Position;
+ totalArea += (positionJ.x + positionI.x) * (positionJ.y - positionI.y);
+
+ j = i;
+ }
+ return -totalArea * 0.5f;
+ }
+
+ public void SetColor(Color32 newColor)
+ {
+ for (int i = 0; i < vertices.Length; i++)
+ {
+ vertices[i].Color = newColor;
+ }
+ }
+
+ public class Vector3ComparerEpsilon : IEqualityComparer
+ {
+ public bool Equals(Vector3 a, Vector3 b)
+ {
+ return Mathf.Abs(a.x - b.x) < EPSILON_LOWER
+ && Mathf.Abs(a.y - b.y) < EPSILON_LOWER
+ && Mathf.Abs(a.z - b.z) < EPSILON_LOWER;
+ }
+
+ public int GetHashCode(Vector3 obj)
+ {
+ // The similarity or difference between two positions can only be calculated if both are supplied
+ // when Distinct is called GetHashCode is used to determine which values are in collision first
+ // therefore we return the same hash code for all values to ensure all comparisons must use
+ // our Equals method to properly determine which values are actually considered equal
+ return 1;
+ }
+ }
+
+ public class VertexComparerEpsilon : IEqualityComparer
+ {
+ public bool Equals(Vertex x, Vertex y)
+ {
+ return x.Position.EqualsWithEpsilon(y.Position);
+ }
+
+ public int GetHashCode(Vertex obj)
+ {
+ // The similarity or difference between two positions can only be calculated if both are supplied
+ // when Distinct is called GetHashCode is used to determine which values are in collision first
+ // therefore we return the same hash code for all values to ensure all comparisons must use
+ // our Equals method to properly determine which values are actually considered equal
+ return 1;
+ }
+ }
+
+ public class PolygonUIDComparer : IEqualityComparer
+ {
+ public bool Equals(Polygon x, Polygon y)
+ {
+ return x.UniqueIndex == y.UniqueIndex;
+ }
+
+ public int GetHashCode(Polygon obj)
+ {
+ return base.GetHashCode();
+ }
+ }
+
+ private const float EPSILON = 0.00001f;
+ private const float EPSILON_LOWER = 0.001f;
+
+ // const float EPSILON_LOWER = 0.003f;
+
+ #region Static Methods
+
+ public enum PolygonPlaneRelation { InFront, Behind, Spanning, Coplanar };
+
+ public static PolygonPlaneRelation TestPolygonAgainstPlane(Polygon polygon, UnityEngine.Plane testPlane)
+ {
+ if (polygon.Plane.normal == testPlane.normal && polygon.Plane.distance == testPlane.distance)
+ {
+ return PolygonPlaneRelation.Coplanar;
+ }
+
+ // Count the number of vertices in front and behind the clip plane
+ int verticesInFront = 0;
+ int verticesBehind = 0;
+
+ for (int i = 0; i < polygon.Vertices.Length; i++)
+ {
+ float distance = testPlane.GetDistanceToPoint(polygon.Vertices[i].Position);
+ if (distance < -EPSILON_LOWER) // Is the point in front of the plane (with thickness)
+ {
+ verticesInFront++;
+ }
+ else if (distance > EPSILON_LOWER) // Is the point behind the plane (with thickness)
+ {
+ verticesBehind++;
+ }
+ }
+
+ if (verticesInFront > 0 && verticesBehind > 0) // If some are in front and some are behind, then the poly spans
+ {
+ return PolygonPlaneRelation.Spanning;
+ }
+ else if (verticesInFront > 0) // Only in front, so entire polygon is in front
+ {
+ return PolygonPlaneRelation.InFront;
+ }
+ else if (verticesBehind > 0) // Only behind, so entire polygon is behind
+ {
+ return PolygonPlaneRelation.Behind;
+ }
+ else // No points in front or behind the plane, so assume coplanar
+ {
+ return PolygonPlaneRelation.Coplanar;
+ }
+ }
+
+ // Loops through the vertices and removes any that share a position with any others so that
+ // only uniquely positioned vertices remain
+ public void RemoveExtraneousVertices()
+ {
+ List newVertices = new List();
+ newVertices.Add(vertices[0]);
+
+ for (int i = 1; i < vertices.Length; i++)
+ {
+ bool alreadyContained = false;
+
+ for (int j = 0; j < newVertices.Count; j++)
+ {
+ if (vertices[i].Position.EqualsWithEpsilonLower(newVertices[j].Position))
+ {
+ alreadyContained = true;
+ break;
+ }
+ }
+
+ if (!alreadyContained)
+ {
+ newVertices.Add(vertices[i]);
+ }
+ }
+
+ vertices = newVertices.ToArray();
+ if (vertices.Length > 2)
+ {
+ CalculatePlane();
+ }
+ }
///
/// Generates the UV coordinates for this polygon automatically. This works similarly to the
@@ -448,255 +464,256 @@ public void GenerateUvCoordinates()
vertices[i].UV = (cancellingRotation * vertices[i].Position) * 0.5f;
}
- public static bool SplitPolygon(Polygon polygon, out Polygon frontPolygon, out Polygon backPolygon, out Vertex newVertex1, out Vertex newVertex2, UnityEngine.Plane clipPlane)
- {
- newVertex1 = null;
- newVertex2 = null;
-
- List frontVertices = new List();
- List backVertices = new List();
-
- for (int i = 0; i < polygon.vertices.Length; i++)
- {
- int previousIndex = i-1;
- if(previousIndex < 0)
- {
- previousIndex = polygon.vertices.Length-1;
- }
-
- Vertex currentVertex = polygon.vertices[i];
- Vertex previousVertex = polygon.vertices[previousIndex];
-
- PointPlaneRelation currentRelation = ComparePointToPlane(currentVertex.Position, clipPlane);
- PointPlaneRelation previousRelation = ComparePointToPlane(previousVertex.Position, clipPlane);
-
- if(previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.InFront)
- {
- // Front add current
- frontVertices.Add(currentVertex);
- }
- else if(previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.InFront)
- {
- float interpolant = Edge.IntersectsPlane(clipPlane, previousVertex.Position, currentVertex.Position);
- Vertex intersection = Vertex.Lerp(previousVertex, currentVertex, interpolant);
-
- // Front add intersection, add current
- frontVertices.Add(intersection);
- frontVertices.Add(currentVertex);
-
- // Back add intersection
- backVertices.Add(intersection.DeepCopy());
-
- newVertex2 = intersection;
- }
- else if(previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.Behind)
- {
- // Reverse order here so that clipping remains consistent for either CW or CCW testing
- float interpolant = Edge.IntersectsPlane(clipPlane, currentVertex.Position, previousVertex.Position);
- Vertex intersection = Vertex.Lerp(currentVertex, previousVertex, interpolant);
-
- // Front add intersection
- frontVertices.Add(intersection);
-
- // Back add intersection, current
- backVertices.Add(intersection.DeepCopy());
- backVertices.Add(currentVertex);
-
- newVertex1 = intersection;
- }
- else if(previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.Behind)
- {
- // Back add current
- backVertices.Add(currentVertex);
-
- }
- else if(currentRelation == PointPlaneRelation.On)
- {
- // Front add current
- frontVertices.Add(currentVertex);
-
- // Back add current
- backVertices.Add(currentVertex.DeepCopy());
-
- if(previousRelation == PointPlaneRelation.InFront)
- {
- newVertex1 = currentVertex;
- }
- else if(previousRelation == PointPlaneRelation.Behind)
- {
- newVertex2 = currentVertex;
- }
- else
- {
-// throw new System.Exception("Unhandled polygon configuration");
- }
- }
- else if(currentRelation == PointPlaneRelation.Behind)
- {
- backVertices.Add(currentVertex);
- }
- else if(currentRelation == PointPlaneRelation.InFront)
- {
- frontVertices.Add(currentVertex);
- }
- else
- {
- throw new System.Exception("Unhandled polygon configuration");
- }
- }
-// Debug.Log("done");
-
- frontPolygon = new Polygon(frontVertices.ToArray(), polygon.Material, polygon.ExcludeFromFinal, polygon.UserExcludeFromFinal, polygon.uniqueIndex);
- backPolygon = new Polygon(backVertices.ToArray(), polygon.Material, polygon.ExcludeFromFinal, polygon.UserExcludeFromFinal, polygon.uniqueIndex);
-
- // Because of some floating point issues and some edge cases relating to splitting the tip of a very thin
- // polygon we can't reliable test that the polygon intersects a plane and will produce two valid pieces
- // so after splitting we need to do an additional test to check that each polygon is valid. If it isn't
- // then we mark that polygon as null and return false to indicate the split wasn't entirely successful
-
- bool splitNecessary = true;
-
- if(frontPolygon.vertices.Length < 3 || frontPolygon.Plane.normal == Vector3.zero)
- {
- frontPolygon = null;
- splitNecessary = false;
- }
-
- if(backPolygon.vertices.Length < 3 || backPolygon.Plane.normal == Vector3.zero)
- {
- backPolygon = null;
- splitNecessary = false;
- }
-
- return splitNecessary;
- }
-
- public static bool PlanePolygonIntersection(Polygon polygon, out Vector3 position1, out Vector3 position2, UnityEngine.Plane testPlane)
- {
- position1 = Vector3.zero;
- position2 = Vector3.zero;
-
- bool position1Set = false;
- bool position2Set = false;
-
- for (int i = 0; i < polygon.vertices.Length; i++)
- {
- int previousIndex = i-1;
- if(previousIndex < 0)
- {
- previousIndex = polygon.vertices.Length-1;
- }
-
- Vertex currentVertex = polygon.vertices[i];
- Vertex previousVertex = polygon.vertices[previousIndex];
-
- PointPlaneRelation currentRelation = ComparePointToPlane(currentVertex.Position, testPlane);
- PointPlaneRelation previousRelation = ComparePointToPlane(previousVertex.Position, testPlane);
-
- if(previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.InFront)
- {
- }
- else if(previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.InFront)
- {
- float interpolant = Edge.IntersectsPlane(testPlane, previousVertex.Position, currentVertex.Position);
- position2 = Vector3.Lerp(previousVertex.Position, currentVertex.Position, interpolant);
- position2Set = true;
- }
- else if(previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.Behind)
- {
- // Reverse order here so that clipping remains consistent for either CW or CCW testing
- float interpolant = Edge.IntersectsPlane(testPlane, currentVertex.Position, previousVertex.Position);
- position1 = Vector3.Lerp(currentVertex.Position, previousVertex.Position, interpolant);
- position1Set = true;
- }
- else if(previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.Behind)
- {
- }
- else if(currentRelation == PointPlaneRelation.On)
- {
- if(previousRelation == PointPlaneRelation.InFront)
- {
- position1 = currentVertex.Position;
- position1Set = true;
- }
- else if(previousRelation == PointPlaneRelation.Behind)
- {
- position2 = currentVertex.Position;
- position2Set = true;
- }
- else
- {
- // throw new System.Exception("Unhandled polygon configuration");
- }
- }
- else if(currentRelation == PointPlaneRelation.Behind)
- {
- }
- else if(currentRelation == PointPlaneRelation.InFront)
- {
- }
- else
- {
- }
- }
-
- return position1Set && position2Set;
- }
-
- public enum PointPlaneRelation { InFront, Behind, On };
-
- public static PointPlaneRelation ComparePointToPlane2(Vector3 point, Plane plane)
- {
- float distance = plane.GetDistanceToPoint(point);
- if (distance < -EPSILON)
- {
- return PointPlaneRelation.InFront;
- }
- else if (distance > EPSILON)
- {
- return PointPlaneRelation.Behind;
- }
- else
- {
- return PointPlaneRelation.On;
- }
- }
-
- public static PointPlaneRelation ComparePointToPlane(Vector3 point, Plane plane)
- {
- float distance = plane.GetDistanceToPoint(point);
- if (distance < -EPSILON_LOWER)
- {
- return PointPlaneRelation.InFront;
- }
- else if (distance > EPSILON_LOWER)
- {
- return PointPlaneRelation.Behind;
- }
- else
- {
- return PointPlaneRelation.On;
- }
- }
-
- public static bool ContainsEdge(Polygon polygon, Edge candidateEdge)
- {
- // Check if any of the edges in the polygon match the candidate edge (including reversed order)
- for (int i = 0; i < polygon.Vertices.Length; i++)
- {
- Vector3 position1 = polygon.Vertices[i].Position;
- Vector3 position2 = polygon.Vertices[(i+1) % polygon.Vertices.Length].Position;
-
- if((candidateEdge.Vertex1.Position == position1 && candidateEdge.Vertex2.Position == position2)
- || (candidateEdge.Vertex2.Position == position1 && candidateEdge.Vertex1.Position == position2))
- {
- return true;
- }
- }
- // None found that matched
- return false;
- }
- #endregion
- }
+ public static bool SplitPolygon(Polygon polygon, out Polygon frontPolygon, out Polygon backPolygon, out Vertex newVertex1, out Vertex newVertex2, UnityEngine.Plane clipPlane)
+ {
+ newVertex1 = null;
+ newVertex2 = null;
+
+ List frontVertices = new List();
+ List backVertices = new List();
+
+ for (int i = 0; i < polygon.vertices.Length; i++)
+ {
+ int previousIndex = i - 1;
+ if (previousIndex < 0)
+ {
+ previousIndex = polygon.vertices.Length - 1;
+ }
+
+ Vertex currentVertex = polygon.vertices[i];
+ Vertex previousVertex = polygon.vertices[previousIndex];
+
+ PointPlaneRelation currentRelation = ComparePointToPlane(currentVertex.Position, clipPlane);
+ PointPlaneRelation previousRelation = ComparePointToPlane(previousVertex.Position, clipPlane);
+
+ if (previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.InFront)
+ {
+ // Front add current
+ frontVertices.Add(currentVertex);
+ }
+ else if (previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.InFront)
+ {
+ float interpolant = Edge.IntersectsPlane(clipPlane, previousVertex.Position, currentVertex.Position);
+ Vertex intersection = Vertex.Lerp(previousVertex, currentVertex, interpolant);
+
+ // Front add intersection, add current
+ frontVertices.Add(intersection);
+ frontVertices.Add(currentVertex);
+
+ // Back add intersection
+ backVertices.Add(intersection.DeepCopy());
+
+ newVertex2 = intersection;
+ }
+ else if (previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.Behind)
+ {
+ // Reverse order here so that clipping remains consistent for either CW or CCW testing
+ float interpolant = Edge.IntersectsPlane(clipPlane, currentVertex.Position, previousVertex.Position);
+ Vertex intersection = Vertex.Lerp(currentVertex, previousVertex, interpolant);
+
+ // Front add intersection
+ frontVertices.Add(intersection);
+
+ // Back add intersection, current
+ backVertices.Add(intersection.DeepCopy());
+ backVertices.Add(currentVertex);
+
+ newVertex1 = intersection;
+ }
+ else if (previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.Behind)
+ {
+ // Back add current
+ backVertices.Add(currentVertex);
+ }
+ else if (currentRelation == PointPlaneRelation.On)
+ {
+ // Front add current
+ frontVertices.Add(currentVertex);
+
+ // Back add current
+ backVertices.Add(currentVertex.DeepCopy());
+
+ if (previousRelation == PointPlaneRelation.InFront)
+ {
+ newVertex1 = currentVertex;
+ }
+ else if (previousRelation == PointPlaneRelation.Behind)
+ {
+ newVertex2 = currentVertex;
+ }
+ else
+ {
+ // throw new System.Exception("Unhandled polygon configuration");
+ }
+ }
+ else if (currentRelation == PointPlaneRelation.Behind)
+ {
+ backVertices.Add(currentVertex);
+ }
+ else if (currentRelation == PointPlaneRelation.InFront)
+ {
+ frontVertices.Add(currentVertex);
+ }
+ else
+ {
+ throw new System.Exception("Unhandled polygon configuration");
+ }
+ }
+ // Debug.Log("done");
+
+ frontPolygon = new Polygon(frontVertices.ToArray(), polygon.Material, polygon.ExcludeFromFinal, polygon.UserExcludeFromFinal, polygon.uniqueIndex);
+ backPolygon = new Polygon(backVertices.ToArray(), polygon.Material, polygon.ExcludeFromFinal, polygon.UserExcludeFromFinal, polygon.uniqueIndex);
+
+ // Because of some floating point issues and some edge cases relating to splitting the tip of a very thin
+ // polygon we can't reliable test that the polygon intersects a plane and will produce two valid pieces
+ // so after splitting we need to do an additional test to check that each polygon is valid. If it isn't
+ // then we mark that polygon as null and return false to indicate the split wasn't entirely successful
+
+ bool splitNecessary = true;
+
+ if (frontPolygon.vertices.Length < 3 || frontPolygon.Plane.normal == Vector3.zero)
+ {
+ frontPolygon = null;
+ splitNecessary = false;
+ }
+
+ if (backPolygon.vertices.Length < 3 || backPolygon.Plane.normal == Vector3.zero)
+ {
+ backPolygon = null;
+ splitNecessary = false;
+ }
+
+ return splitNecessary;
+ }
+
+ public static bool PlanePolygonIntersection(Polygon polygon, out Vector3 position1, out Vector3 position2, UnityEngine.Plane testPlane)
+ {
+ position1 = Vector3.zero;
+ position2 = Vector3.zero;
+
+ bool position1Set = false;
+ bool position2Set = false;
+
+ for (int i = 0; i < polygon.vertices.Length; i++)
+ {
+ int previousIndex = i - 1;
+ if (previousIndex < 0)
+ {
+ previousIndex = polygon.vertices.Length - 1;
+ }
+
+ Vertex currentVertex = polygon.vertices[i];
+ Vertex previousVertex = polygon.vertices[previousIndex];
+
+ PointPlaneRelation currentRelation = ComparePointToPlane(currentVertex.Position, testPlane);
+ PointPlaneRelation previousRelation = ComparePointToPlane(previousVertex.Position, testPlane);
+
+ if (previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.InFront)
+ {
+ }
+ else if (previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.InFront)
+ {
+ float interpolant = Edge.IntersectsPlane(testPlane, previousVertex.Position, currentVertex.Position);
+ position2 = Vector3.Lerp(previousVertex.Position, currentVertex.Position, interpolant);
+ position2Set = true;
+ }
+ else if (previousRelation == PointPlaneRelation.InFront && currentRelation == PointPlaneRelation.Behind)
+ {
+ // Reverse order here so that clipping remains consistent for either CW or CCW testing
+ float interpolant = Edge.IntersectsPlane(testPlane, currentVertex.Position, previousVertex.Position);
+ position1 = Vector3.Lerp(currentVertex.Position, previousVertex.Position, interpolant);
+ position1Set = true;
+ }
+ else if (previousRelation == PointPlaneRelation.Behind && currentRelation == PointPlaneRelation.Behind)
+ {
+ }
+ else if (currentRelation == PointPlaneRelation.On)
+ {
+ if (previousRelation == PointPlaneRelation.InFront)
+ {
+ position1 = currentVertex.Position;
+ position1Set = true;
+ }
+ else if (previousRelation == PointPlaneRelation.Behind)
+ {
+ position2 = currentVertex.Position;
+ position2Set = true;
+ }
+ else
+ {
+ // throw new System.Exception("Unhandled polygon configuration");
+ }
+ }
+ else if (currentRelation == PointPlaneRelation.Behind)
+ {
+ }
+ else if (currentRelation == PointPlaneRelation.InFront)
+ {
+ }
+ else
+ {
+ }
+ }
+
+ return position1Set && position2Set;
+ }
+
+ public enum PointPlaneRelation { InFront, Behind, On };
+
+ public static PointPlaneRelation ComparePointToPlane2(Vector3 point, Plane plane)
+ {
+ float distance = plane.GetDistanceToPoint(point);
+ if (distance < -EPSILON)
+ {
+ return PointPlaneRelation.InFront;
+ }
+ else if (distance > EPSILON)
+ {
+ return PointPlaneRelation.Behind;
+ }
+ else
+ {
+ return PointPlaneRelation.On;
+ }
+ }
+
+ public static PointPlaneRelation ComparePointToPlane(Vector3 point, Plane plane)
+ {
+ float distance = plane.GetDistanceToPoint(point);
+ if (distance < -EPSILON_LOWER)
+ {
+ return PointPlaneRelation.InFront;
+ }
+ else if (distance > EPSILON_LOWER)
+ {
+ return PointPlaneRelation.Behind;
+ }
+ else
+ {
+ return PointPlaneRelation.On;
+ }
+ }
+
+ public static bool ContainsEdge(Polygon polygon, Edge candidateEdge)
+ {
+ // Check if any of the edges in the polygon match the candidate edge (including reversed order)
+ for (int i = 0; i < polygon.Vertices.Length; i++)
+ {
+ Vector3 position1 = polygon.Vertices[i].Position;
+ Vector3 position2 = polygon.Vertices[(i + 1) % polygon.Vertices.Length].Position;
+
+ if ((candidateEdge.Vertex1.Position == position1 && candidateEdge.Vertex2.Position == position2)
+ || (candidateEdge.Vertex2.Position == position1 && candidateEdge.Vertex1.Position == position2))
+ {
+ return true;
+ }
+ }
+ // None found that matched
+ return false;
+ }
+
+ #endregion Static Methods
+ }
}
+
#endif
\ No newline at end of file
diff --git a/Scripts/Core/CSGBuildSettings.cs b/Scripts/Core/CSGBuildSettings.cs
index e96085c2..14b2919c 100755
--- a/Scripts/Core/CSGBuildSettings.cs
+++ b/Scripts/Core/CSGBuildSettings.cs
@@ -1,35 +1,39 @@
#if UNITY_EDITOR || RUNTIME_CSG
+
using UnityEngine;
using System.Collections;
using UnityEngine.Rendering;
namespace Sabresaurus.SabreCSG
{
- [System.Serializable]
- public class CSGBuildSettings
- {
- // Whether to also do a collision pass
- public bool GenerateCollisionMeshes = true;
+ [System.Serializable]
+ public class CSGBuildSettings
+ {
+ // Whether to also do a collision pass
+ public bool GenerateCollisionMeshes = true;
- // Also calculate tangents (needed for Unity's built in bump mapping)
- public bool GenerateTangents = true;
+ // Also calculate tangents (needed for Unity's built in bump mapping)
+ public bool GenerateTangents = true;
- public bool OptimizeGeometry = true;
+ public bool OptimizeGeometry = false;
public bool SaveMeshesAsAssets = false;
// Generate a UV2 channel for lightmapping
[Tooltip("Note that when enabled this will have a non-trivial increase in build times")]
- public bool GenerateLightmapUVs = false;
-
- // Unwrap settings, note these are different to what is displayed in the Model Importer, see http://docs.unity3d.com/401/Documentation/Manual/LightmappingUV.html
- [Range(0f,1f)]
- public float UnwrapAngleError = 0.08f; // 0 to 1
- [Range(0f,1f)]
- public float UnwrapAreaError = 0.15f; // 0 to 1
- [Range(0f,180f)]
- public float UnwrapHardAngle = 88f; // degrees, 0 to 180
- public float UnwrapPackMargin = 0.00390625f; // Assumes a 1024 texture, pack margin = PadPixels / 1024
+ public bool GenerateLightmapUVs = false;
+
+ // Unwrap settings, note these are different to what is displayed in the Model Importer, see http://docs.unity3d.com/401/Documentation/Manual/LightmappingUV.html
+ [Range(0f, 1f)]
+ public float UnwrapAngleError = 0.08f; // 0 to 1
+
+ [Range(0f, 1f)]
+ public float UnwrapAreaError = 0.15f; // 0 to 1
+
+ [Range(0f, 180f)]
+ public float UnwrapHardAngle = 88f; // degrees, 0 to 180
+
+ public float UnwrapPackMargin = 0.00390625f; // Assumes a 1024 texture, pack margin = PadPixels / 1024
/*
Fix most of the light leaking that occurs after CSG operations.
@@ -40,7 +44,7 @@ In Unity even a simple box placement like below will cause dynamic light to leak
Dynamic
Light Source
- X
+ X
X +------+
X | |
X | |
@@ -75,85 +79,92 @@ block the incoming light and prevent the light leak artifact from appearing.
*/
public ShadowCastingMode ShadowCastingMode = ShadowCastingMode.TwoSided;
+ public ReflectionProbeUsage ReflectionProbeUsage = ReflectionProbeUsage.BlendProbes;
+
+ // What default physics material to use on collision meshes
+ public PhysicMaterial DefaultPhysicsMaterial = null;
+
+ public Material DefaultVisualMaterial = null;
+
+ [HideInInspector]
+ public bool IsBuilt = false; // Only really relevant to last build settings
+
+ public CSGBuildSettings ShallowCopy()
+ {
+ return (CSGBuildSettings)this.MemberwiseClone();
+ }
- // What default physics material to use on collision meshes
- public PhysicMaterial DefaultPhysicsMaterial = null;
-
- public Material DefaultVisualMaterial = null;
-
- [HideInInspector]
- public bool IsBuilt = false; // Only really relevant to last build settings
-
- public CSGBuildSettings ShallowCopy()
- {
- return (CSGBuildSettings)this.MemberwiseClone();
- }
-
- ///
- /// Compares two build settings and returns if they are practially different.
- ///
- public static bool AreDifferent(CSGBuildSettings settings1, CSGBuildSettings settings2)
- {
- if(settings1.GenerateCollisionMeshes != settings2.GenerateCollisionMeshes)
- {
- return true;
- }
- if(settings1.GenerateTangents != settings2.GenerateTangents)
- {
- return true;
- }
+ ///
+ /// Compares two build settings and returns if they are practially different.
+ ///
+ public static bool AreDifferent(CSGBuildSettings settings1, CSGBuildSettings settings2)
+ {
+ if (settings1.GenerateCollisionMeshes != settings2.GenerateCollisionMeshes)
+ {
+ return true;
+ }
+ if (settings1.GenerateTangents != settings2.GenerateTangents)
+ {
+ return true;
+ }
if (settings1.OptimizeGeometry != settings2.OptimizeGeometry)
{
return true;
}
if (settings1.GenerateLightmapUVs != settings2.GenerateLightmapUVs)
- {
- return true;
- }
-
- // Only compare UV unwrap settings if unwrapping is in use
- if(settings1.GenerateLightmapUVs && settings1.GenerateLightmapUVs)
- {
- if(settings1.UnwrapAngleError != settings2.UnwrapAngleError)
- {
- return true;
- }
-
- if(settings1.UnwrapAreaError != settings2.UnwrapAreaError)
- {
- return true;
- }
-
- if(settings1.UnwrapHardAngle != settings2.UnwrapHardAngle)
- {
- return true;
- }
-
- if(settings1.UnwrapPackMargin != settings2.UnwrapPackMargin)
- {
- return true;
- }
- }
-
- if(settings1.DefaultPhysicsMaterial != settings2.DefaultPhysicsMaterial)
- {
- return true;
- }
- if(settings1.DefaultVisualMaterial != settings2.DefaultVisualMaterial)
- {
- return true;
- }
-
- if(settings1.ShadowCastingMode != settings2.ShadowCastingMode)
{
return true;
}
- // Don't compare IsBuilt
+ // Only compare UV unwrap settings if unwrapping is in use
+ if (settings1.GenerateLightmapUVs && settings1.GenerateLightmapUVs)
+ {
+ if (settings1.UnwrapAngleError != settings2.UnwrapAngleError)
+ {
+ return true;
+ }
+
+ if (settings1.UnwrapAreaError != settings2.UnwrapAreaError)
+ {
+ return true;
+ }
+
+ if (settings1.UnwrapHardAngle != settings2.UnwrapHardAngle)
+ {
+ return true;
+ }
+
+ if (settings1.UnwrapPackMargin != settings2.UnwrapPackMargin)
+ {
+ return true;
+ }
+ }
+
+ if (settings1.DefaultPhysicsMaterial != settings2.DefaultPhysicsMaterial)
+ {
+ return true;
+ }
+ if (settings1.DefaultVisualMaterial != settings2.DefaultVisualMaterial)
+ {
+ return true;
+ }
+
+ if (settings1.ShadowCastingMode != settings2.ShadowCastingMode)
+ {
+ return true;
+ }
+
+ if (settings1.ReflectionProbeUsage != settings2.ReflectionProbeUsage)
+ {
+ return true;
+ }
- // No practical differences found
- return false;
- }
- }
+ // Don't compare IsBuilt
+
+ // No practical differences found
+ return false;
+ }
+ }
}
+
#endif
\ No newline at end of file
diff --git a/Scripts/CurrentSettings.cs b/Scripts/CurrentSettings.cs
index 5c7e8fc4..cb4eab99 100644
--- a/Scripts/CurrentSettings.cs
+++ b/Scripts/CurrentSettings.cs
@@ -162,6 +162,18 @@ public static bool ShowBrushBoundsGuideLines
}
}
+ public static bool ShowHiddenGameObjectsInHierarchy
+ {
+ get
+ {
+ return PlayerPrefs.GetInt(KEY_PREFIX + "ShowHiddenGameObjectsInHierarchy", 0) != 0;
+ }
+ set
+ {
+ PlayerPrefs.SetInt(KEY_PREFIX + "ShowHiddenGameObjectsInHierarchy", value ? 1 : 0);
+ }
+ }
+
public static bool ReducedHandleThreshold
{
get
diff --git a/Scripts/Editor/AssetBrowserContextMenu.cs b/Scripts/Editor/AssetBrowserContextMenu.cs
new file mode 100644
index 00000000..9fb23c53
--- /dev/null
+++ b/Scripts/Editor/AssetBrowserContextMenu.cs
@@ -0,0 +1,52 @@
+#if UNITY_EDITOR
+
+using System;
+using UnityEngine;
+using UnityEditor;
+
+namespace Sabresaurus.SabreCSG
+{
+ ///
+ /// Provides the context menus for the asset browser.
+ ///
+ ///
+ public class AssetBrowserContextMenu : UnityEditor.EditorWindow
+ {
+ [MenuItem("Assets/SabreCSG/Create Material For Texture(s)")]
+ private static void CreateMaterialForTextures()
+ {
+ // get all selected textures in the asset browser.
+ Texture2D[] textures = Array.ConvertAll(Selection.GetFiltered(typeof(Texture2D), SelectionMode.Assets), item => (Texture2D)item);
+
+ // iterate through each selected texture:
+ for (int i = 0; i < textures.Length; i++)
+ {
+ Texture2D texture = textures[i];
+
+ EditorUtility.DisplayProgressBar("SabreCSG: Creating Material For Texture(s)", "Creating Material '" + texture.name + "'...", i / (float)textures.Length);
+
+ // create a material asset for the texture.
+ Material material = new Material(Shader.Find("Standard"));
+ material.SetTexture("_MainTex", texture);
+ material.SetFloat("_Glossiness", 0.0f);
+
+ // get the directory path.
+ string path = System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(texture)) + "\\";
+ string file = System.IO.Path.GetFileNameWithoutExtension(AssetDatabase.GetAssetPath(texture)) + ".mat";
+
+ AssetDatabase.CreateAsset(material, path + file);
+ }
+
+ EditorUtility.ClearProgressBar();
+ }
+
+ [MenuItem("Assets/SabreCSG/Create Material For Texture(s)", true)]
+ private static bool IsCreateMaterialForTexturesEnabled()
+ {
+ // must have a texture selected in the asset browser.
+ return Selection.GetFiltered(typeof(Texture2D), SelectionMode.Assets).Length > 0;
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Scripts/Editor/AssetBrowserContextMenu.cs.meta b/Scripts/Editor/AssetBrowserContextMenu.cs.meta
new file mode 100644
index 00000000..3815e028
--- /dev/null
+++ b/Scripts/Editor/AssetBrowserContextMenu.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 2a5d7e1aa8392e5439f9d91fc7200b06
+timeCreated: 1527256810
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Editor/Inspectors/BrushBaseInspector.cs b/Scripts/Editor/Inspectors/BrushBaseInspector.cs
index bca1f7f3..9292fe8c 100644
--- a/Scripts/Editor/Inspectors/BrushBaseInspector.cs
+++ b/Scripts/Editor/Inspectors/BrushBaseInspector.cs
@@ -77,10 +77,78 @@ public sealed override void OnInspectorGUI()
}
}
+ // volume editing:
+ if (BrushTargets.Any(b => b.Mode == CSGMode.Volume))
+ {
+ using (new NamedVerticalScope("Volume"))
+ {
+ // find all of the volume types in the project:
+ List volumeTypes = Volume.FindAllInAssembly();
+ if (volumeTypes.Count == 0)
+ {
+ EditorGUILayout.LabelField("No volume types could be found!");
+ }
+ else
+ {
+ // find all of the volume brushes that are currently selected.
+ BrushBase[] volumeBrushes = BrushTargets.Where(b => b.Mode == CSGMode.Volume).ToArray();
+ BrushBase volumeTarget = volumeBrushes.Contains(BrushTarget) ? BrushTarget : volumeBrushes.Last();
+
+ // make sure all volume brushes are of the same type (for multi-editing).
+ System.Type brushTargetVolumeType = volumeTarget.Volume ? volumeTarget.Volume.GetType() : null;
+ if (volumeBrushes.Length > 1 && !volumeBrushes.All(b => b.Volume.GetType() == brushTargetVolumeType))
+ {
+ EditorGUILayout.LabelField("Cannot multi-edit volumes of different types!");
+ }
+ else
+ {
+ // let the user pick a volume type:
+ int selected = 0;
+ if (volumeTarget.Volume != null)
+ {
+ for (int i = 0; i < volumeTypes.Count; i++)
+ {
+ selected = i;
+ if (volumeTarget.Volume.GetType() == volumeTypes[i])
+ break;
+ }
+ }
+ selected = EditorGUILayout.Popup("Volume Type", selected, volumeTypes.Select(v => v.Name + " (" + v.Namespace + ")").ToArray());
+
+ // set the brush volume type:
+ for (int i = 0; i < volumeBrushes.Length; i++)
+ {
+ BrushBase target = volumeBrushes[i];
+
+ // if the brush does not have a volume yet or the wrong one, create the selected type now:
+ if (target.Volume == null || target.Volume.GetType() != volumeTypes[selected])
+ {
+ target.Volume = (Volume)ScriptableObject.CreateInstance(volumeTypes[selected]);
+ if (serializedObject.targetObject != null)
+ {
+ serializedObject.ApplyModifiedProperties();
+ System.Array.ForEach(volumeBrushes, item => item.Invalidate(true));
+ }
+ }
+ }
+
+ // custom volume inspector:
+ if (volumeTarget.Volume.OnInspectorGUI(volumeBrushes.Select(b => b.Volume).ToArray()))
+ {
+ if (serializedObject.targetObject != null)
+ {
+ serializedObject.ApplyModifiedProperties();
+ System.Array.ForEach(volumeBrushes, item => item.Invalidate(true));
+ }
+ }
+ }
+ }
+ }
+ }
+
// custom inspector:
DoInspectorGUI();
-
// generic brush editing:
using (new NamedVerticalScope("Order"))
{
@@ -188,4 +256,4 @@ public sealed override void OnInspectorGUI()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/Scripts/Editor/Inspectors/CSGModelInspector.cs b/Scripts/Editor/Inspectors/CSGModelInspector.cs
index 7948db4b..e8745b12 100644
--- a/Scripts/Editor/Inspectors/CSGModelInspector.cs
+++ b/Scripts/Editor/Inspectors/CSGModelInspector.cs
@@ -24,6 +24,8 @@ public class CSGModelInspector : Editor
private SerializedProperty shadowCastingModeProperty;
+ private SerializedProperty reflectionProbeUsageProperty;
+
private SerializedProperty defaultPhysicsMaterialProperty;
private SerializedProperty defaultVisualMaterialProperty;
@@ -34,7 +36,6 @@ public class CSGModelInspector : Editor
// Temporary importer settings.
- private static int importerValveMapFormatScale = 32;
private static int importerUnrealGoldScale = 64;
public void OnEnable()
@@ -53,6 +54,8 @@ public void OnEnable()
shadowCastingModeProperty = serializedObject.FindProperty("buildSettings.ShadowCastingMode");
+ reflectionProbeUsageProperty = serializedObject.FindProperty("buildSettings.ReflectionProbeUsage");
+
defaultPhysicsMaterialProperty = serializedObject.FindProperty("buildSettings.DefaultPhysicsMaterial");
defaultVisualMaterialProperty = serializedObject.FindProperty("buildSettings.DefaultVisualMaterial");
@@ -93,6 +96,7 @@ public override void OnInspectorGUI()
GUI.enabled = true;
EditorGUILayout.PropertyField(shadowCastingModeProperty, new GUIContent("Shadow Casting Mode"));
+ EditorGUILayout.PropertyField(reflectionProbeUsageProperty, new GUIContent("Reflection Probes"));
// Experimental build settings to enable features that are not yet completely stable
GUILayout.Label("Experimental", EditorStyles.boldLabel);
@@ -220,9 +224,6 @@ public override void OnInspectorGUI()
GuiLayoutBeginImporterSection(SabreCSGResources.ImporterImporterValveMapFormat2006Texture, "Source Engine 2006 Importer", "Henry de Jongh");
- importerValveMapFormatScale = EditorGUILayout.IntField("Scale", importerValveMapFormatScale);
- if (importerValveMapFormatScale < 1) importerValveMapFormatScale = 1;
-
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("Import Source Engine Map (*.vmf)"))
{
@@ -234,19 +235,19 @@ public override void OnInspectorGUI()
EditorUtility.DisplayProgressBar("SabreCSG: Importing Source Engine Map", "Parsing Valve Map Format File (*.vmf)...", 0.0f);
var importer = new Importers.ValveMapFormat2006.VmfImporter();
var map = importer.Import(path);
- Importers.ValveMapFormat2006.VmfWorldConverter.Import(csgModel, map, importerValveMapFormatScale);
+ Importers.ValveMapFormat2006.VmfWorldConverter.Import(csgModel, map);
}
}
catch (Exception ex)
{
EditorUtility.ClearProgressBar();
- EditorUtility.DisplayDialog("Source Engine Map Import", "An exception occurred while importing the map:\r\n" + ex.Message + ex.StackTrace, "Ohno!");
+ EditorUtility.DisplayDialog("Source Engine Map Import", "An exception occurred while importing the map:\r\n" + ex.Message, "Ohno!");
}
}
if (GUILayout.Button("?", GUILayout.Width(16)))
{
- EditorUtility.DisplayDialog("Source Engine 2006 Importer", "This importer was created using Source SDK maps and Hammer 4.1.\n\nImportant Notes:\n* It will try to find the materials in your project automatically. First it looks for the full name with forward slashes '/' replaced by periods '.' like 'BRICK.BRICKFLOOR001A' then the last word 'BRICKFLOOR001A'. The latter option could cause some false positives, try creating a material with the full name if this happens.\n\nKnown Issues:\n* Some UVs' panning appears to be completely off but I have been unable to replicate it. If anyone knows why it happens please do contact me!", "Okay");
+ EditorUtility.DisplayDialog("Source Engine 2006 Importer", "This importer was created using Source SDK maps and Hammer 4.1.\n\nImportant Notes:\n* It will try to find the materials in your project automatically. First it looks for the full name with forward slashes '/' replaced by periods '.' like 'BRICK.BRICKFLOOR001A' then the last word 'BRICKFLOOR001A'. The latter option could cause some false positives, try creating a material with the full name if this happens.", "Okay");
}
EditorGUILayout.EndHorizontal();
diff --git a/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs b/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs
index 603dd9a4..e12cb058 100644
--- a/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs
+++ b/Scripts/Editor/Inspectors/PrimitiveBrushInspector.cs
@@ -6,90 +6,95 @@
namespace Sabresaurus.SabreCSG
{
- [CanEditMultipleObjects]
+ [CanEditMultipleObjects]
[CustomEditor(typeof(PrimitiveBrush))]
- public class PrimitiveBrushInspector : BrushBaseInspector
+ public class PrimitiveBrushInspector : BrushBaseInspector
{
- string scaleString = "1";
- string resizeString = "1";
+ private string scaleString = "1";
+ private string resizeString = "1";
- Mesh sourceMesh = null;
+ private Mesh sourceMesh = null;
- SerializedProperty prismSideCountProp;
- SerializedProperty cylinderSideCountProp;
- SerializedProperty sphereSideCountProp;
- SerializedProperty icoSphereIterationCountProp;
- SerializedProperty coneSideCountProp;
+ private SerializedProperty prismSideCountProp;
+ private SerializedProperty cylinderSideCountProp;
+ private SerializedProperty sphereSideCountProp;
+ private SerializedProperty icoSphereIterationCountProp;
+ private SerializedProperty coneSideCountProp;
+ private SerializedProperty capsuleSideCountProp;
+ private SerializedProperty capsuleHeightProp;
- float shellDistance = 0;
+ private float shellDistance = 0;
- protected override void OnEnable ()
- {
- base.OnEnable ();
- // Setup the SerializedProperties.
- prismSideCountProp = serializedObject.FindProperty ("prismSideCount");
+ protected override void OnEnable()
+ {
+ base.OnEnable();
+ // Setup the SerializedProperties.
+ prismSideCountProp = serializedObject.FindProperty("prismSideCount");
+
+ cylinderSideCountProp = serializedObject.FindProperty("cylinderSideCount");
+
+ sphereSideCountProp = serializedObject.FindProperty("sphereSideCount");
+
+ coneSideCountProp = serializedObject.FindProperty("coneSideCount");
+
+ capsuleSideCountProp = serializedObject.FindProperty("capsuleSideCount");
+ capsuleHeightProp = serializedObject.FindProperty("capsuleHeight");
+
+ icoSphereIterationCountProp = serializedObject.FindProperty("icoSphereIterationCount");
+ }
+
+ private void ChangeBrushesToType(PrimitiveBrushType newType)
+ {
+ Undo.RecordObjects(targets, "Change Brush Type");
+ PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
+ foreach (PrimitiveBrush brush in brushes)
+ {
+ Bounds localBounds = brush.GetBounds();
+ brush.BrushType = newType;
+ brush.ResetPolygons();
- cylinderSideCountProp = serializedObject.FindProperty ("cylinderSideCount");
+ if (localBounds.size != new Vector3(2, 2, 2))
+ {
+ BrushUtility.Resize(brush, localBounds.size);
+ }
+ else
+ {
+ brush.Invalidate(true);
+ }
+ }
+ }
- sphereSideCountProp = serializedObject.FindProperty ("sphereSideCount");
+ private void ResetPolygonsKeepScale()
+ {
+ PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
+ foreach (PrimitiveBrush brush in brushes)
+ {
+ Bounds localBounds = brush.GetBounds();
+ brush.ResetPolygons();
- coneSideCountProp = serializedObject.FindProperty ("coneSideCount");
+ if (localBounds.size != new Vector3(2, 2, 2))
+ {
+ BrushUtility.Resize(brush, localBounds.size);
+ }
+ else
+ {
+ brush.Invalidate(true);
+ }
+ }
+ }
- icoSphereIterationCountProp = serializedObject.FindProperty ("icoSphereIterationCount");
+ private void ResetBounds()
+ {
+ // Reset the bounds to a 2,2,2 cube
+ Undo.RecordObjects(targets, "Reset Bounds");
+ PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
+ foreach (PrimitiveBrush brush in brushes)
+ {
+ BrushUtility.Resize(brush, new Vector3(2, 2, 2));
+ }
}
- private void ChangeBrushesToType(PrimitiveBrushType newType)
- {
- Undo.RecordObjects(targets, "Change Brush Type");
- PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
- foreach (PrimitiveBrush brush in brushes)
- {
- Bounds localBounds = brush.GetBounds();
- brush.BrushType = newType;
- brush.ResetPolygons();
-
- if(localBounds.size != new Vector3(2, 2, 2))
- {
- BrushUtility.Resize(brush, localBounds.size);
- }
- else
- {
- brush.Invalidate(true);
- }
- }
- }
-
- private void ResetPolygonsKeepScale()
- {
- PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
- foreach (PrimitiveBrush brush in brushes)
- {
- Bounds localBounds = brush.GetBounds();
- brush.ResetPolygons();
-
- if(localBounds.size != new Vector3(2, 2, 2))
- {
- BrushUtility.Resize(brush, localBounds.size);
- }
- else
- {
- brush.Invalidate(true);
- }
- }
- }
-
- private void ResetBounds()
- {
- // Reset the bounds to a 2,2,2 cube
- Undo.RecordObjects(targets, "Reset Bounds");
- PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
- foreach (PrimitiveBrush brush in brushes)
- {
- BrushUtility.Resize(brush, new Vector3(2, 2, 2));
- }
- }
-
- void DrawBrushButton(PrimitiveBrushType brushType, PrimitiveBrushType? activeType, GUIStyle brushButtonStyle, GUIStyle labelStyle, int width, int height, bool shortMode)
+ private void DrawBrushButton(PrimitiveBrushType brushType, PrimitiveBrushType? activeType, GUIStyle brushButtonStyle, GUIStyle labelStyle, int width, int height, bool shortMode)
{
GUI.enabled = !activeType.HasValue || activeType.Value != brushType;
if (GUILayout.Button(new GUIContent(" ", SabreCSGResources.GetButtonTexture(brushType)), brushButtonStyle, GUILayout.Width(width), GUILayout.Height(height)))
@@ -108,17 +113,22 @@ void DrawBrushButton(PrimitiveBrushType brushType, PrimitiveBrushType? activeTyp
name = "Cyl";
}
+ if (shortMode && brushType == PrimitiveBrushType.Capsule)
+ {
+ name = "Cap";
+ }
+
GUI.Label(lastRect, name, labelStyle);
}
public override void DoInspectorGUI()
{
float drawableWidth = EditorGUIUtility.currentViewWidth;
- drawableWidth -= 42; // Take some off for scroll bars and padding
+ drawableWidth -= 42; // Take some off for scroll bars and padding
- PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => ((PrimitiveBrush)item).BrushType).ToArray();
+ PrimitiveBrushType[] selectedTypes = BrushTargets.Select(item => ((PrimitiveBrush)item).BrushType).ToArray();
- PrimitiveBrushType? activeType = (selectedTypes.Length == 1) ? (PrimitiveBrushType?)selectedTypes[0] : null;
+ PrimitiveBrushType? activeType = (selectedTypes.Length == 1) ? (PrimitiveBrushType?)selectedTypes[0] : null;
using (new NamedVerticalScope("Type"))
{
@@ -144,11 +154,12 @@ public override void DoInspectorGUI()
DrawBrushButton(PrimitiveBrushType.Cylinder, activeType, brushButtonStyle, labelStyle, stretchButtonWidth, buttonHeight, shortMode);
DrawBrushButton(PrimitiveBrushType.Sphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);
DrawBrushButton(PrimitiveBrushType.IcoSphere, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);
-
+
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal();
DrawBrushButton(PrimitiveBrushType.Cone, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);
+ DrawBrushButton(PrimitiveBrushType.Capsule, activeType, brushButtonStyle, labelStyle, buttonWidth, buttonHeight, shortMode);
GUI.enabled = true; // Reset GUI enabled so that the next items aren't disabled
GUILayout.EndHorizontal();
@@ -189,6 +200,13 @@ public override void DoInspectorGUI()
{
EditorGUILayout.PropertyField(coneSideCountProp, new GUIContent("Sides"));
}
+ else if (activeType.Value == PrimitiveBrushType.Capsule)
+ {
+ EditorGUILayout.BeginVertical();
+ EditorGUILayout.PropertyField(capsuleHeightProp, new GUIContent("Height"));
+ EditorGUILayout.PropertyField(capsuleSideCountProp, new GUIContent("Sides"));
+ EditorGUILayout.EndVertical();
+ }
if (EditorGUI.EndChangeCheck())
{
// One of the properties has changed
@@ -273,42 +291,41 @@ public override void DoInspectorGUI()
GUILayout.EndHorizontal();
}
- using (new NamedVerticalScope("Rotation"))
- {
-
- GUILayout.Label("Align up direction", EditorStyles.boldLabel);
- GUILayout.BeginHorizontal();
- if(GUILayout.Button("X"))
- {
- AlignUpToAxis(new Vector3(1, 0, 0), false);
- }
- if(GUILayout.Button("Y"))
- {
- AlignUpToAxis(new Vector3(0, 1, 0), false);
- }
- if(GUILayout.Button("Z"))
- {
- AlignUpToAxis(new Vector3(0, 0, 1), false);
- }
- GUILayout.EndHorizontal();
-
- GUILayout.Label("Align up direction (keep positions)", EditorStyles.boldLabel);
-
- GUILayout.BeginHorizontal();
- if(GUILayout.Button("X"))
- {
- AlignUpToAxis(new Vector3(1, 0, 0), true);
- }
- if(GUILayout.Button("Y"))
- {
- AlignUpToAxis(new Vector3(0, 1, 0), true);
- }
- if(GUILayout.Button("Z"))
- {
- AlignUpToAxis(new Vector3(0, 0, 1), true);
- }
- GUILayout.EndHorizontal();
- }
+ using (new NamedVerticalScope("Rotation"))
+ {
+ GUILayout.Label("Align up direction", EditorStyles.boldLabel);
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("X"))
+ {
+ AlignUpToAxis(new Vector3(1, 0, 0), false);
+ }
+ if (GUILayout.Button("Y"))
+ {
+ AlignUpToAxis(new Vector3(0, 1, 0), false);
+ }
+ if (GUILayout.Button("Z"))
+ {
+ AlignUpToAxis(new Vector3(0, 0, 1), false);
+ }
+ GUILayout.EndHorizontal();
+
+ GUILayout.Label("Align up direction (keep positions)", EditorStyles.boldLabel);
+
+ GUILayout.BeginHorizontal();
+ if (GUILayout.Button("X"))
+ {
+ AlignUpToAxis(new Vector3(1, 0, 0), true);
+ }
+ if (GUILayout.Button("Y"))
+ {
+ AlignUpToAxis(new Vector3(0, 1, 0), true);
+ }
+ if (GUILayout.Button("Z"))
+ {
+ AlignUpToAxis(new Vector3(0, 0, 1), true);
+ }
+ GUILayout.EndHorizontal();
+ }
using (new NamedVerticalScope("Misc"))
{
@@ -389,7 +406,6 @@ public override void DoInspectorGUI()
BrushUtility.SplitIntersecting(brushes);
}
-
// BrushOrder brushOrder = BrushTarget.GetBrushOrder();
// string positionString = string.Join(",", brushOrder.Position.Select(item => item.ToString()).ToArray());
// GUILayout.Label(positionString, EditorStyles.boldLabel);
@@ -403,7 +419,7 @@ public override void DoInspectorGUI()
//}
}
- base.DoInspectorGUI();
+ base.DoInspectorGUI();
}
///
@@ -411,17 +427,17 @@ public override void DoInspectorGUI()
///
/// Axis to match transform.up to
/// If specified, when the brush is rotated the vertices will be counter rotated so they remain in their old positions and orientations
- void AlignUpToAxis(Vector3 newUpAxis, bool counterAlignment)
- {
- PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
- foreach (PrimitiveBrush brush in brushes)
- {
+ private void AlignUpToAxis(Vector3 newUpAxis, bool counterAlignment)
+ {
+ PrimitiveBrush[] brushes = BrushTargets.Cast().ToArray();
+ foreach (PrimitiveBrush brush in brushes)
+ {
Vector3 perpendicularDirection = Vector3.up;
- if(Mathf.Abs(Vector3.Dot(perpendicularDirection, newUpAxis)) > 0.9f)
+ if (Mathf.Abs(Vector3.Dot(perpendicularDirection, newUpAxis)) > 0.9f)
{
perpendicularDirection = Vector3.back;
}
-
+
// Calculate the new rotation for the brush so that it's new transform.up is at the axis. Note the parameter order
Quaternion rotation = Quaternion.LookRotation(-perpendicularDirection, newUpAxis);
Quaternion inverseRotation = Quaternion.Inverse(rotation * Quaternion.Inverse(brush.transform.localRotation));
@@ -433,28 +449,28 @@ void AlignUpToAxis(Vector3 newUpAxis, bool counterAlignment)
// If they want to realign the brush but keep original world positions
if (counterAlignment)
- {
- Polygon[] polygons = brush.GetPolygons();
-
- for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++)
- {
- Vertex[] vertices = polygons[polygonIndex].Vertices;
-
- // Rotate positions and vertices so they remain in their original place
- for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++)
- {
- vertices[vertexIndex].Position = inverseRotation * vertices[vertexIndex].Position;
- vertices[vertexIndex].Normal = inverseRotation * vertices[vertexIndex].Normal;
- }
- }
-
- // Polygon vertices have changed, so recalculate the planes
- for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++)
- {
- polygons[polygonIndex].CalculatePlane();
- }
- }
- }
- }
+ {
+ Polygon[] polygons = brush.GetPolygons();
+
+ for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++)
+ {
+ Vertex[] vertices = polygons[polygonIndex].Vertices;
+
+ // Rotate positions and vertices so they remain in their original place
+ for (int vertexIndex = 0; vertexIndex < vertices.Length; vertexIndex++)
+ {
+ vertices[vertexIndex].Position = inverseRotation * vertices[vertexIndex].Position;
+ vertices[vertexIndex].Normal = inverseRotation * vertices[vertexIndex].Normal;
+ }
+ }
+
+ // Polygon vertices have changed, so recalculate the planes
+ for (int polygonIndex = 0; polygonIndex < polygons.Length; polygonIndex++)
+ {
+ polygons[polygonIndex].CalculatePlane();
+ }
+ }
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Extensions/EditorHelper.cs b/Scripts/Extensions/EditorHelper.cs
index 511c4137..48185c8f 100644
--- a/Scripts/Extensions/EditorHelper.cs
+++ b/Scripts/Extensions/EditorHelper.cs
@@ -375,6 +375,14 @@ public static bool DuplicateSelection()
newObjects[i] = Selection.activeGameObject;
// Remove the 'Brush (1)', 'Brush (2)', etc. from the name.
newObjects[i].name = Regex.Replace(newObjects[i].name, " \\(\\d+\\)$", "");
+
+ // If we are dealing with a brush, properly duplicate the volume type.
+ BrushBase brush = newObjects[i].GetComponent();
+ if (brush != null && brush.Volume != null)
+ {
+ brush.Volume = ScriptableObject.Instantiate(brush.Volume);
+ brush.RebuildVolume();
+ }
}
// Finished duplicating, select all new objects
Selection.objects = newObjects;
diff --git a/Scripts/Extensions/Extensions.cs b/Scripts/Extensions/Extensions.cs
index 0c366006..2bf70e6f 100644
--- a/Scripts/Extensions/Extensions.cs
+++ b/Scripts/Extensions/Extensions.cs
@@ -428,5 +428,16 @@ internal static string ToGeneratedHierarchyString(this Bounds bounds)
string z = MathHelper.RoundFloat(bounds.size.z, 0.0001f).ToString(CultureInfo.InvariantCulture);
return x + " x " + y + " x " + z;
}
+
+ ///
+ /// Determines whether the layer mask contains the specified layer.
+ ///
+ /// The layer mask.
+ /// The layer to check for.
+ /// true if the layer mask contains the specified layer; otherwise, false.
+ internal static bool Contains(this LayerMask layerMask, int layer)
+ {
+ return layerMask == (layerMask | (1 << layer));
+ }
}
}
\ No newline at end of file
diff --git a/Scripts/Extensions/MathHelper.cs b/Scripts/Extensions/MathHelper.cs
index ab2db6f8..729ff92e 100644
--- a/Scripts/Extensions/MathHelper.cs
+++ b/Scripts/Extensions/MathHelper.cs
@@ -312,18 +312,18 @@ public static bool PlaneEqualsLooser(Plane plane1, Plane plane2)
public static bool PlaneEqualsLooserWithFlip(Plane plane1, Plane plane2)
{
if (
- Mathf.Abs(plane1.distance - plane2.distance) < EPSILON_LOWER_3
- && Mathf.Abs(plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_2
- && Mathf.Abs(plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_2
- && Mathf.Abs(plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_2)
+ Mathf.Abs(plane1.distance - plane2.distance) <= 0.08f
+ && Mathf.Abs(plane1.normal.x - plane2.normal.x) < 0.006f
+ && Mathf.Abs(plane1.normal.y - plane2.normal.y) < 0.006f
+ && Mathf.Abs(plane1.normal.z - plane2.normal.z) < 0.006f)
{
return true;
}
else if (
- Mathf.Abs(-plane1.distance - plane2.distance) < EPSILON_LOWER_3
- && Mathf.Abs(-plane1.normal.x - plane2.normal.x) < EPSILON_LOWER_2
- && Mathf.Abs(-plane1.normal.y - plane2.normal.y) < EPSILON_LOWER_2
- && Mathf.Abs(-plane1.normal.z - plane2.normal.z) < EPSILON_LOWER_2)
+ Mathf.Abs(-plane1.distance - plane2.distance) <= 0.08f
+ && Mathf.Abs(-plane1.normal.x - plane2.normal.x) < 0.006f
+ && Mathf.Abs(-plane1.normal.y - plane2.normal.y) < 0.006f
+ && Mathf.Abs(-plane1.normal.z - plane2.normal.z) < 0.006f)
{
return true;
}
diff --git a/Scripts/Extensions/TransformHelper.cs b/Scripts/Extensions/TransformHelper.cs
index 7f4827a8..7a237229 100644
--- a/Scripts/Extensions/TransformHelper.cs
+++ b/Scripts/Extensions/TransformHelper.cs
@@ -108,6 +108,10 @@ public static void UngroupSelection()
}
}
- }
+ public static Transform[] FindChildren(this Transform transform, string name)
+ {
+ return transform.GetComponentsInChildren().Where(t => t.name == name).ToArray();
+ }
+ }
}
#endif
\ No newline at end of file
diff --git a/Scripts/Geometry/BrushFactory.cs b/Scripts/Geometry/BrushFactory.cs
index 4104e332..cd4b7d35 100644
--- a/Scripts/Geometry/BrushFactory.cs
+++ b/Scripts/Geometry/BrushFactory.cs
@@ -1,4 +1,5 @@
#if UNITY_EDITOR || RUNTIME_CSG
+
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
@@ -6,279 +7,278 @@
namespace Sabresaurus.SabreCSG
{
- ///
- /// Provides easy methods for generating brushes (as Polygon sets)
- ///
- public static class BrushFactory
- {
- ///
- /// Generates a cube (size 2,2,2)
- ///
- /// Polygons to be supplied to a brush.
- public static Polygon[] GenerateCube()
- {
- Polygon[] polygons = new Polygon[6];
-
- // Polygons and vertices are created in a clockwise order
-
- // Polygons are created in this order: back, left, front, right, bottom, top
-
- // Vertices with those polygons are created in this order: BR, BL, TL, TR (when viewed aligned with the UV)
-
- // Therefore to move the two bottom vertices of the left face, you would pick the second face (index 1) and
- // then manipulate the first two vertices (indexes 0 and 1)
-
- // Back
- polygons[0] = new Polygon(new Vertex[] {
- new Vertex(new Vector3(-1, -1, 1), new Vector3(0, 0, 1), new Vector2(1,0)),
- new Vertex(new Vector3(1, -1, 1), new Vector3(0, 0, 1), new Vector2(0,0)),
- new Vertex(new Vector3(1, 1, 1), new Vector3(0, 0, 1), new Vector2(0,1)),
- new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 0, 1), new Vector2(1,1)),
- }, null, false, false);
-
- // Left
- polygons[1] = new Polygon(new Vertex[] {
- new Vertex(new Vector3(-1, -1, -1), new Vector3(-1, 0, 0), new Vector2(1,0)),
- new Vertex(new Vector3(-1, -1, 1), new Vector3(-1, 0, 0), new Vector2(0,0)),
- new Vertex(new Vector3(-1, 1, 1), new Vector3(-1, 0, 0), new Vector2(0,1)),
- new Vertex(new Vector3(-1, 1, -1), new Vector3(-1, 0, 0), new Vector2(1,1)),
- }, null, false, false);
-
- // Front
- polygons[2] = new Polygon(new Vertex[] {
- new Vertex(new Vector3(1, -1, 1), new Vector3(1, 0, 0), new Vector2(1,0)),
- new Vertex(new Vector3(1, -1, -1), new Vector3(1, 0, 0), new Vector2(0,0)),
- new Vertex(new Vector3(1, 1, -1), new Vector3(1, 0, 0), new Vector2(0,1)),
- new Vertex(new Vector3(1, 1, 1), new Vector3(1, 0, 0), new Vector2(1,1)),
- }, null, false, false);
-
- // Right
- polygons[3] = new Polygon(new Vertex[] {
- new Vertex(new Vector3(1, -1, -1), new Vector3(0, 0, -1), new Vector2(1,0)),
- new Vertex(new Vector3(-1, -1, -1), new Vector3(0, 0, -1), new Vector2(0,0)),
- new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 0, -1), new Vector2(0,1)),
- new Vertex(new Vector3(1, 1, -1), new Vector3(0, 0, -1), new Vector2(1,1)),
- }, null, false, false);
-
- // Bottom
- polygons[4] = new Polygon(new Vertex[] {
- new Vertex(new Vector3(-1, -1, -1), new Vector3(0, -1, 0), new Vector2(1,0)),
- new Vertex(new Vector3(1, -1, -1), new Vector3(0, -1, 0), new Vector2(0,0)),
- new Vertex(new Vector3(1, -1, 1), new Vector3(0, -1, 0), new Vector2(0,1)),
- new Vertex(new Vector3(-1, -1, 1), new Vector3(0, -1, 0), new Vector2(1,1)),
- }, null, false, false);
-
- // Top
- polygons[5] = new Polygon(new Vertex[] {
- new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,0)),
- new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,0)),
- new Vertex(new Vector3(1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,1)),
- new Vertex(new Vector3(1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,1)),
- }, null, false, false);
-
- return polygons;
- }
-
- ///
- /// Generates a cylinder of height and radius 2, unlike a prism sides have smooth normals
- ///
- /// Polygons to be supplied to a brush.
- /// Side count for the cylinder.
- public static Polygon[] GenerateCylinder(int sideCount = 20)
- {
- Polygon[] polygons = new Polygon[sideCount * 3];
-
- float angleDelta = Mathf.PI * 2 / sideCount;
-
- for (int i = 0; i < sideCount; i++)
- {
- polygons[i] = new Polygon(new Vertex[]
- {
- new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)),
- new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)),
- new Vector2(i * (1f/sideCount),0)),
- new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)),
- new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)),
- new Vector2((i+1) * (1f/sideCount),0)),
- new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)),
- new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)),
- new Vector2((i+1) * (1f/sideCount),1)),
- new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)),
- new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)),
- new Vector2(i * (1f/sideCount),1)),
- }, null, false, false);
- }
-
- Vertex capCenterVertex = new Vertex(new Vector3(0,1,0), Vector3.up, new Vector2(0,0));
-
- for (int i = 0; i < sideCount; i++)
- {
- Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
- Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta)));
-
- Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
- polygons[sideCount + i] = new Polygon(capVertices, null, false, false);
- }
-
- capCenterVertex = new Vertex(new Vector3(0,-1,0), Vector3.down, new Vector2(0,0));
-
- for (int i = 0; i < sideCount; i++)
- {
- Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
- Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * -angleDelta), -1, Mathf.Cos((i+1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta)));
-
- Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
- polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false);
- }
-
- return polygons;
- }
-
- ///
- /// Generates a prism of height and radius 2, unlike a cylinder sides have faceted normals
- ///
- /// Polygons to be supplied to a brush.
- /// Side count for the prism.
- public static Polygon[] GeneratePrism(int sideCount)
- {
- Polygon[] polygons = new Polygon[sideCount * 3];
-
- float angleDelta = Mathf.PI * 2 / sideCount;
-
- for (int i = 0; i < sideCount; i++)
- {
- Vector3 normal = new Vector3(Mathf.Sin((i+0.5f) * angleDelta), 0, Mathf.Cos((i+0.5f) * angleDelta));
- polygons[i] = new Polygon(new Vertex[] {
-
- new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)),
- normal,
- new Vector2(0,0)),
- new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)),
- normal,
- new Vector2(1,0)),
- new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)),
- normal,
- new Vector2(1,1)),
- new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)),
- normal,
- new Vector2(0,1)),
- }, null, false, false);
- }
-
- Vertex capCenterVertex = new Vertex(new Vector3(0,1,0), Vector3.up, new Vector2(0,0));
-
- for (int i = 0; i < sideCount; i++)
- {
- Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
- Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta)));
-
- Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
- polygons[sideCount + i] = new Polygon(capVertices, null, false, false);
- }
-
- capCenterVertex = new Vertex(new Vector3(0,-1,0), Vector3.down, new Vector2(0,0));
-
- for (int i = 0; i < sideCount; i++)
- {
- Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
- Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i+1) * -angleDelta), -1, Mathf.Cos((i+1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i+1) * angleDelta), Mathf.Cos((i+1) * angleDelta)));
-
- Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
- polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false);
- }
-
- return polygons;
- }
-
- [System.Obsolete("Use GeneratePolarSphere instead, or for a more isotropic geometry use GenerateIcoSphere")]
- public static Polygon[] GenerateSphere(int lateralCount = 6, int longitudinalCount = 12)
- {
- return GeneratePolarSphere(lateralCount, longitudinalCount);
- }
-
- ///
- /// Generates an ico-sphere of radius 2. Unlike a polar-sphere this has a more even distribution of vertices.
- ///
- /// Polygons to be supplied to a brush.
- /// Number of times the surface is subdivided, values of 1 or 2 are recommended.
- public static Polygon[] GenerateIcoSphere(int iterationCount)
- {
- // Derived from http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
- float longestDimension = (1+Mathf.Sqrt(5f)) / 2f;
- Vector3 sourceVector = new Vector3(0, 1, longestDimension);
-
- // Make the longest dimension 1, so the icosphere fits in a 2,2,2 cube
- sourceVector.Normalize();
-
- Vertex[] vertices = new Vertex[]
- {
- new Vertex(new Vector3(-sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(-sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
-
- new Vertex(new Vector3(sourceVector.x,-sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(sourceVector.x,+sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(sourceVector.x,-sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(sourceVector.x,+sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero),
-
- new Vertex(new Vector3(+sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(+sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(-sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero),
- new Vertex(new Vector3(-sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero),
- };
-
- Polygon[] polygons = new Polygon[]
- {
- new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[1].DeepCopy(),vertices[7].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[5].DeepCopy(),vertices[1].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[7].DeepCopy(),vertices[10].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[10].DeepCopy(),vertices[11].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[11].DeepCopy(),vertices[5].DeepCopy()}, null, false, false),
-
- new Polygon(new Vertex[] { vertices[7].DeepCopy(),vertices[1].DeepCopy(),vertices[8].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[1].DeepCopy(),vertices[5].DeepCopy(),vertices[9].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[10].DeepCopy(),vertices[7].DeepCopy(),vertices[6].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[11].DeepCopy(),vertices[10].DeepCopy(),vertices[2].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[5].DeepCopy(),vertices[11].DeepCopy(),vertices[4].DeepCopy()}, null, false, false),
-
- new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[2].DeepCopy(),vertices[6].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[4].DeepCopy(),vertices[2].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[6].DeepCopy(),vertices[8].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[8].DeepCopy(),vertices[9].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[9].DeepCopy(),vertices[4].DeepCopy()}, null, false, false),
-
- new Polygon(new Vertex[] { vertices[6].DeepCopy(),vertices[2].DeepCopy(),vertices[10].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[2].DeepCopy(),vertices[4].DeepCopy(),vertices[11].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[8].DeepCopy(),vertices[6].DeepCopy(),vertices[7].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[9].DeepCopy(),vertices[8].DeepCopy(),vertices[1].DeepCopy()}, null, false, false),
- new Polygon(new Vertex[] { vertices[4].DeepCopy(),vertices[9].DeepCopy(),vertices[5].DeepCopy()}, null, false, false),
- };
-
- // Refine
- for (int i = 0; i < iterationCount; i++)
- {
- Polygon[] newPolygons = new Polygon[polygons.Length*4];
- for (int j = 0; j < polygons.Length; j++)
- {
- Vertex a = Vertex.Lerp(polygons[j].Vertices[0], polygons[j].Vertices[1], 0.5f);
- Vertex b = Vertex.Lerp(polygons[j].Vertices[1], polygons[j].Vertices[2], 0.5f);
- Vertex c = Vertex.Lerp(polygons[j].Vertices[2], polygons[j].Vertices[0], 0.5f);
-
- a.Position = a.Position.normalized;
- b.Position = b.Position.normalized;
- c.Position = c.Position.normalized;
-
- newPolygons[j*4+0] = new Polygon(new Vertex[] { polygons[j].Vertices[0].DeepCopy(), a.DeepCopy(), c.DeepCopy()}, null, false, false);
- newPolygons[j*4+1] = new Polygon(new Vertex[] { polygons[j].Vertices[1].DeepCopy(), b.DeepCopy(), a.DeepCopy()}, null, false, false);
- newPolygons[j*4+2] = new Polygon(new Vertex[] { polygons[j].Vertices[2].DeepCopy(), c.DeepCopy(), b.DeepCopy()}, null, false, false);
- newPolygons[j*4+3] = new Polygon(new Vertex[] { a.DeepCopy(), b.DeepCopy(), c.DeepCopy()}, null, false, false);
- }
- polygons = newPolygons;
- }
-
- for (int i = 0; i < polygons.Length; i++)
- {
+ ///
+ /// Provides easy methods for generating brushes (as Polygon sets)
+ ///
+ public static class BrushFactory
+ {
+ ///
+ /// Generates a cube (size 2,2,2)
+ ///
+ /// Polygons to be supplied to a brush.
+ public static Polygon[] GenerateCube()
+ {
+ Polygon[] polygons = new Polygon[6];
+
+ // Polygons and vertices are created in a clockwise order
+
+ // Polygons are created in this order: back, left, front, right, bottom, top
+
+ // Vertices with those polygons are created in this order: BR, BL, TL, TR (when viewed aligned with the UV)
+
+ // Therefore to move the two bottom vertices of the left face, you would pick the second face (index 1) and
+ // then manipulate the first two vertices (indexes 0 and 1)
+
+ // Back
+ polygons[0] = new Polygon(new Vertex[] {
+ new Vertex(new Vector3(-1, -1, 1), new Vector3(0, 0, 1), new Vector2(1,0)),
+ new Vertex(new Vector3(1, -1, 1), new Vector3(0, 0, 1), new Vector2(0,0)),
+ new Vertex(new Vector3(1, 1, 1), new Vector3(0, 0, 1), new Vector2(0,1)),
+ new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 0, 1), new Vector2(1,1)),
+ }, null, false, false);
+
+ // Left
+ polygons[1] = new Polygon(new Vertex[] {
+ new Vertex(new Vector3(-1, -1, -1), new Vector3(-1, 0, 0), new Vector2(1,0)),
+ new Vertex(new Vector3(-1, -1, 1), new Vector3(-1, 0, 0), new Vector2(0,0)),
+ new Vertex(new Vector3(-1, 1, 1), new Vector3(-1, 0, 0), new Vector2(0,1)),
+ new Vertex(new Vector3(-1, 1, -1), new Vector3(-1, 0, 0), new Vector2(1,1)),
+ }, null, false, false);
+
+ // Front
+ polygons[2] = new Polygon(new Vertex[] {
+ new Vertex(new Vector3(1, -1, 1), new Vector3(1, 0, 0), new Vector2(1,0)),
+ new Vertex(new Vector3(1, -1, -1), new Vector3(1, 0, 0), new Vector2(0,0)),
+ new Vertex(new Vector3(1, 1, -1), new Vector3(1, 0, 0), new Vector2(0,1)),
+ new Vertex(new Vector3(1, 1, 1), new Vector3(1, 0, 0), new Vector2(1,1)),
+ }, null, false, false);
+
+ // Right
+ polygons[3] = new Polygon(new Vertex[] {
+ new Vertex(new Vector3(1, -1, -1), new Vector3(0, 0, -1), new Vector2(1,0)),
+ new Vertex(new Vector3(-1, -1, -1), new Vector3(0, 0, -1), new Vector2(0,0)),
+ new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 0, -1), new Vector2(0,1)),
+ new Vertex(new Vector3(1, 1, -1), new Vector3(0, 0, -1), new Vector2(1,1)),
+ }, null, false, false);
+
+ // Bottom
+ polygons[4] = new Polygon(new Vertex[] {
+ new Vertex(new Vector3(-1, -1, -1), new Vector3(0, -1, 0), new Vector2(1,0)),
+ new Vertex(new Vector3(1, -1, -1), new Vector3(0, -1, 0), new Vector2(0,0)),
+ new Vertex(new Vector3(1, -1, 1), new Vector3(0, -1, 0), new Vector2(0,1)),
+ new Vertex(new Vector3(-1, -1, 1), new Vector3(0, -1, 0), new Vector2(1,1)),
+ }, null, false, false);
+
+ // Top
+ polygons[5] = new Polygon(new Vertex[] {
+ new Vertex(new Vector3(-1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,0)),
+ new Vertex(new Vector3(-1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,0)),
+ new Vertex(new Vector3(1, 1, 1), new Vector3(0, 1, 0), new Vector2(0,1)),
+ new Vertex(new Vector3(1, 1, -1), new Vector3(0, 1, 0), new Vector2(1,1)),
+ }, null, false, false);
+
+ return polygons;
+ }
+
+ ///
+ /// Generates a cylinder of height and radius 2, unlike a prism sides have smooth normals
+ ///
+ /// Polygons to be supplied to a brush.
+ /// Side count for the cylinder.
+ public static Polygon[] GenerateCylinder(int sideCount = 20)
+ {
+ Polygon[] polygons = new Polygon[sideCount * 3];
+
+ float angleDelta = Mathf.PI * 2 / sideCount;
+
+ for (int i = 0; i < sideCount; i++)
+ {
+ polygons[i] = new Polygon(new Vertex[]
+ {
+ new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)),
+ new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)),
+ new Vector2(i * (1f/sideCount),0)),
+ new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)),
+ new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)),
+ new Vector2((i+1) * (1f/sideCount),0)),
+ new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)),
+ new Vector3(Mathf.Sin((i+1) * angleDelta), 0, Mathf.Cos((i+1) * angleDelta)),
+ new Vector2((i+1) * (1f/sideCount),1)),
+ new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)),
+ new Vector3(Mathf.Sin(i * angleDelta), 0, Mathf.Cos(i * angleDelta)),
+ new Vector2(i * (1f/sideCount),1)),
+ }, null, false, false);
+ }
+
+ Vertex capCenterVertex = new Vertex(new Vector3(0, 1, 0), Vector3.up, new Vector2(0, 0));
+
+ for (int i = 0; i < sideCount; i++)
+ {
+ Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
+ Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * angleDelta), 1, Mathf.Cos((i + 1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta)));
+
+ Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
+ polygons[sideCount + i] = new Polygon(capVertices, null, false, false);
+ }
+
+ capCenterVertex = new Vertex(new Vector3(0, -1, 0), Vector3.down, new Vector2(0, 0));
+
+ for (int i = 0; i < sideCount; i++)
+ {
+ Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
+ Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * -angleDelta), -1, Mathf.Cos((i + 1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta)));
+
+ Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
+ polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false);
+ }
+
+ return polygons;
+ }
+
+ ///
+ /// Generates a prism of height and radius 2, unlike a cylinder sides have faceted normals
+ ///
+ /// Polygons to be supplied to a brush.
+ /// Side count for the prism.
+ public static Polygon[] GeneratePrism(int sideCount)
+ {
+ Polygon[] polygons = new Polygon[sideCount * 3];
+
+ float angleDelta = Mathf.PI * 2 / sideCount;
+
+ for (int i = 0; i < sideCount; i++)
+ {
+ Vector3 normal = new Vector3(Mathf.Sin((i + 0.5f) * angleDelta), 0, Mathf.Cos((i + 0.5f) * angleDelta));
+ polygons[i] = new Polygon(new Vertex[] {
+ new Vertex(new Vector3(Mathf.Sin(i * angleDelta), -1, Mathf.Cos(i * angleDelta)),
+ normal,
+ new Vector2(0,0)),
+ new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), -1, Mathf.Cos((i+1) * angleDelta)),
+ normal,
+ new Vector2(1,0)),
+ new Vertex(new Vector3(Mathf.Sin((i+1) * angleDelta), 1, Mathf.Cos((i+1) * angleDelta)),
+ normal,
+ new Vector2(1,1)),
+ new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)),
+ normal,
+ new Vector2(0,1)),
+ }, null, false, false);
+ }
+
+ Vertex capCenterVertex = new Vertex(new Vector3(0, 1, 0), Vector3.up, new Vector2(0, 0));
+
+ for (int i = 0; i < sideCount; i++)
+ {
+ Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * angleDelta), 1, Mathf.Cos(i * angleDelta)), Vector3.up, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
+ Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * angleDelta), 1, Mathf.Cos((i + 1) * angleDelta)), Vector3.up, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta)));
+
+ Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
+ polygons[sideCount + i] = new Polygon(capVertices, null, false, false);
+ }
+
+ capCenterVertex = new Vertex(new Vector3(0, -1, 0), Vector3.down, new Vector2(0, 0));
+
+ for (int i = 0; i < sideCount; i++)
+ {
+ Vertex vertex1 = new Vertex(new Vector3(Mathf.Sin(i * -angleDelta), -1, Mathf.Cos(i * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin(i * angleDelta), Mathf.Cos(i * angleDelta)));
+ Vertex vertex2 = new Vertex(new Vector3(Mathf.Sin((i + 1) * -angleDelta), -1, Mathf.Cos((i + 1) * -angleDelta)), Vector3.down, new Vector2(Mathf.Sin((i + 1) * angleDelta), Mathf.Cos((i + 1) * angleDelta)));
+
+ Vertex[] capVertices = new Vertex[] { vertex1, vertex2, capCenterVertex.DeepCopy() };
+ polygons[sideCount * 2 + i] = new Polygon(capVertices, null, false, false);
+ }
+
+ return polygons;
+ }
+
+ [System.Obsolete("Use GeneratePolarSphere instead, or for a more isotropic geometry use GenerateIcoSphere")]
+ public static Polygon[] GenerateSphere(int lateralCount = 6, int longitudinalCount = 12)
+ {
+ return GeneratePolarSphere(lateralCount, longitudinalCount);
+ }
+
+ ///
+ /// Generates an ico-sphere of radius 2. Unlike a polar-sphere this has a more even distribution of vertices.
+ ///
+ /// Polygons to be supplied to a brush.
+ /// Number of times the surface is subdivided, values of 1 or 2 are recommended.
+ public static Polygon[] GenerateIcoSphere(int iterationCount)
+ {
+ // Derived from http://blog.andreaskahler.com/2009/06/creating-icosphere-mesh-in-code.html
+ float longestDimension = (1 + Mathf.Sqrt(5f)) / 2f;
+ Vector3 sourceVector = new Vector3(0, 1, longestDimension);
+
+ // Make the longest dimension 1, so the icosphere fits in a 2,2,2 cube
+ sourceVector.Normalize();
+
+ Vertex[] vertices = new Vertex[]
+ {
+ new Vertex(new Vector3(-sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(sourceVector.y,+sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(-sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(sourceVector.y,-sourceVector.z,sourceVector.x), Vector3.zero, Vector2.zero),
+
+ new Vertex(new Vector3(sourceVector.x,-sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(sourceVector.x,+sourceVector.y,+sourceVector.z), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(sourceVector.x,-sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(sourceVector.x,+sourceVector.y,-sourceVector.z), Vector3.zero, Vector2.zero),
+
+ new Vertex(new Vector3(+sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(+sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(-sourceVector.z,sourceVector.x,-sourceVector.y), Vector3.zero, Vector2.zero),
+ new Vertex(new Vector3(-sourceVector.z,sourceVector.x,+sourceVector.y), Vector3.zero, Vector2.zero),
+ };
+
+ Polygon[] polygons = new Polygon[]
+ {
+ new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[1].DeepCopy(),vertices[7].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[5].DeepCopy(),vertices[1].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[7].DeepCopy(),vertices[10].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[10].DeepCopy(),vertices[11].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[0].DeepCopy(),vertices[11].DeepCopy(),vertices[5].DeepCopy()}, null, false, false),
+
+ new Polygon(new Vertex[] { vertices[7].DeepCopy(),vertices[1].DeepCopy(),vertices[8].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[1].DeepCopy(),vertices[5].DeepCopy(),vertices[9].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[10].DeepCopy(),vertices[7].DeepCopy(),vertices[6].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[11].DeepCopy(),vertices[10].DeepCopy(),vertices[2].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[5].DeepCopy(),vertices[11].DeepCopy(),vertices[4].DeepCopy()}, null, false, false),
+
+ new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[2].DeepCopy(),vertices[6].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[4].DeepCopy(),vertices[2].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[6].DeepCopy(),vertices[8].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[8].DeepCopy(),vertices[9].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[3].DeepCopy(),vertices[9].DeepCopy(),vertices[4].DeepCopy()}, null, false, false),
+
+ new Polygon(new Vertex[] { vertices[6].DeepCopy(),vertices[2].DeepCopy(),vertices[10].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[2].DeepCopy(),vertices[4].DeepCopy(),vertices[11].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[8].DeepCopy(),vertices[6].DeepCopy(),vertices[7].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[9].DeepCopy(),vertices[8].DeepCopy(),vertices[1].DeepCopy()}, null, false, false),
+ new Polygon(new Vertex[] { vertices[4].DeepCopy(),vertices[9].DeepCopy(),vertices[5].DeepCopy()}, null, false, false),
+ };
+
+ // Refine
+ for (int i = 0; i < iterationCount; i++)
+ {
+ Polygon[] newPolygons = new Polygon[polygons.Length * 4];
+ for (int j = 0; j < polygons.Length; j++)
+ {
+ Vertex a = Vertex.Lerp(polygons[j].Vertices[0], polygons[j].Vertices[1], 0.5f);
+ Vertex b = Vertex.Lerp(polygons[j].Vertices[1], polygons[j].Vertices[2], 0.5f);
+ Vertex c = Vertex.Lerp(polygons[j].Vertices[2], polygons[j].Vertices[0], 0.5f);
+
+ a.Position = a.Position.normalized;
+ b.Position = b.Position.normalized;
+ c.Position = c.Position.normalized;
+
+ newPolygons[j * 4 + 0] = new Polygon(new Vertex[] { polygons[j].Vertices[0].DeepCopy(), a.DeepCopy(), c.DeepCopy() }, null, false, false);
+ newPolygons[j * 4 + 1] = new Polygon(new Vertex[] { polygons[j].Vertices[1].DeepCopy(), b.DeepCopy(), a.DeepCopy() }, null, false, false);
+ newPolygons[j * 4 + 2] = new Polygon(new Vertex[] { polygons[j].Vertices[2].DeepCopy(), c.DeepCopy(), b.DeepCopy() }, null, false, false);
+ newPolygons[j * 4 + 3] = new Polygon(new Vertex[] { a.DeepCopy(), b.DeepCopy(), c.DeepCopy() }, null, false, false);
+ }
+ polygons = newPolygons;
+ }
+
+ for (int i = 0; i < polygons.Length; i++)
+ {
bool anyAboveHalf = false;
for (int j = 0; j < polygons[i].Vertices.Length; j++)
@@ -302,7 +302,7 @@ public static Polygon[] GenerateIcoSphere(int iterationCount)
}
}
- if(u > 0.75f)
+ if (u > 0.75f)
{
anyAboveHalf = true;
}
@@ -325,154 +325,150 @@ public static Polygon[] GenerateIcoSphere(int iterationCount)
uv.x += 1;
}
polygons[i].Vertices[j].UV = uv;
+ }
+ }
+ }
+
+ return polygons;
+ }
+
+ ///
+ /// Generates a sphere of radius 2
+ ///
+ /// Polygons to be supplied to a brush.
+ /// Vertex count up from the south pole to the north pole.
+ /// Vertex count around the sphere equator.
+ public static Polygon[] GeneratePolarSphere(int lateralCount = 6, int longitudinalCount = 12)
+ {
+ Polygon[] polygons = new Polygon[lateralCount * longitudinalCount];
+
+ float angleDelta = 1f / lateralCount;
+ float longitudinalDelta = 1f / longitudinalCount;
+
+ // Generate tris for the top and bottom, then quads for the rest
+ for (int i = 0; i < lateralCount; i++)
+ {
+ for (int j = 0; j < longitudinalCount; j++)
+ {
+ Vertex[] vertices;
+
+ if (i == lateralCount - 1)
+ {
+ vertices = new Vertex[] {
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))),
+ };
+ }
+ else if (i > 0)
+ {
+ vertices = new Vertex[] {
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))),
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))),
+ };
+ }
+ else // i == 0
+ {
+ vertices = new Vertex[] {
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * i * angleDelta),
+ Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
+ ),
+ new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
+ new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
+ Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
+ Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
+ ),
+ new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))),
+ };
+ }
+ for (int d = 0; d < vertices.Length; d++)
+ {
+ vertices[d].UV = new Vector2(vertices[d].UV.y, 1 - vertices[d].UV.x);
}
+
+ polygons[i + j * lateralCount] = new Polygon(vertices, null, false, false);
}
}
- return polygons;
- }
-
- ///
- /// Generates a sphere of radius 2
- ///
- /// Polygons to be supplied to a brush.
- /// Vertex count up from the south pole to the north pole.
- /// Vertex count around the sphere equator.
- public static Polygon[] GeneratePolarSphere(int lateralCount = 6, int longitudinalCount = 12)
- {
- Polygon[] polygons = new Polygon[lateralCount * longitudinalCount];
-
- float angleDelta = 1f / lateralCount;
- float longitudinalDelta = 1f / longitudinalCount;
-
- // Generate tris for the top and bottom, then quads for the rest
- for (int i = 0; i < lateralCount; i++)
- {
- for (int j = 0; j < longitudinalCount; j++)
- {
- Vertex[] vertices;
-
- if(i == lateralCount-1)
- {
- vertices = new Vertex[] {
-
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))),
- };
- }
- else if(i > 0)
- {
- vertices = new Vertex[] {
-
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))),
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector2(i * (1f/lateralCount), j * (1f/longitudinalCount))),
- };
- }
- else // i == 0
- {
- vertices = new Vertex[] {
-
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * i * angleDelta),
- Mathf.Sin(Mathf.PI * i * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector2(i * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * (j+1) * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * (j+1) * longitudinalDelta)
- ),
- new Vector2((i+1) * (1f/lateralCount), (j+1) * (1f/longitudinalCount))),
- new Vertex(new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector3(Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Cos(2 * Mathf.PI * j * longitudinalDelta),
- Mathf.Cos(Mathf.PI * (i+1) * angleDelta),
- Mathf.Sin(Mathf.PI * (i+1) * angleDelta) * Mathf.Sin(2 * Mathf.PI * j * longitudinalDelta)
- ),
- new Vector2((i+1) * (1f/lateralCount), j * (1f/longitudinalCount))),
- };
- }
-
- for (int d = 0; d < vertices.Length; d++)
- {
- vertices[d].UV = new Vector2(vertices[d].UV.y, 1 - vertices[d].UV.x);
- }
-
- polygons[i + j * lateralCount] = new Polygon(vertices, null, false, false);
- }
- }
-
- return polygons;
- }
+ return polygons;
+ }
///
/// Generates a cone of height and radius 2. If the is 3, generates a triangular-based pyramid.
@@ -549,131 +545,278 @@ public static Polygon[] GenerateCone(int sideCount = 20)
return polygons;
}
+ ///
+ /// Generates a capsule.
+ ///
+ /// The height of the capsule.
+ /// The radius of the capsule.
+ /// The amount of segments of the capsule.
+ /// Special Thanks: Jay Kay for your capsule generation algorithm https://forum.unity.com/threads/solved-closed-procedurally-generated-capsule.406982/#post-2652094.
+ /// Polygons to be supplied to a brush.
+ public static Polygon[] GenerateCapsule(float height = 2.0f, float radius = 0.5f, int segments = 24)
+ {
+ // make segments an even number
+ if (segments % 2 != 0)
+ segments++;
+
+ // extra vertex on the seam
+ int points = segments + 1;
+
+ // calculate points around a circle
+ float[] pX = new float[points];
+ float[] pZ = new float[points];
+ float[] pY = new float[points];
+ float[] pR = new float[points];
+
+ float calcH = 0f;
+ float calcV = 0f;
+
+ for (int i = 0; i < points; i++)
+ {
+ pX[i] = Mathf.Sin(calcH * Mathf.Deg2Rad);
+ pZ[i] = Mathf.Cos(calcH * Mathf.Deg2Rad);
+ pY[i] = Mathf.Cos(calcV * Mathf.Deg2Rad);
+ pR[i] = Mathf.Sin(calcV * Mathf.Deg2Rad);
+
+ calcH += 360f / (float)segments;
+ calcV += 180f / (float)segments;
+ }
+
+ // - Vertices and UVs -
+
+ Vector3[] vertices = new Vector3[points * (points + 1)];
+ Vector2[] uvs = new Vector2[vertices.Length];
+ int ind = 0;
+
+ // Y-offset is half the height minus the diameter
+ float yOff = (height - (radius * 2f)) * 0.5f;
+ if (yOff < 0)
+ yOff = 0;
+
+ // uv calculations
+ float stepX = 1f / ((float)(points - 1));
+ float uvX, uvY;
+
+ // Top Hemisphere
+ int top = Mathf.CeilToInt((float)points * 0.5f);
+
+ for (int y = 0; y < top; y++)
+ {
+ for (int x = 0; x < points; x++)
+ {
+ vertices[ind] = new Vector3(pX[x] * pR[y], pY[y], pZ[x] * pR[y]) * radius;
+ vertices[ind].y = yOff + vertices[ind].y;
+
+ uvX = 1f - (stepX * (float)x);
+ uvY = (vertices[ind].y + (height * 0.5f)) / height;
+ uvs[ind] = new Vector2(uvX, uvY);
+
+ ind++;
+ }
+ }
+
+ // Bottom Hemisphere
+ int btm = Mathf.FloorToInt((float)points * 0.5f);
+
+ for (int y = btm; y < points; y++)
+ {
+ for (int x = 0; x < points; x++)
+ {
+ vertices[ind] = new Vector3(pX[x] * pR[y], pY[y], pZ[x] * pR[y]) * radius;
+ vertices[ind].y = -yOff + vertices[ind].y;
+
+ uvX = 1f - (stepX * (float)x);
+ uvY = (vertices[ind].y + (height * 0.5f)) / height;
+ uvs[ind] = new Vector2(uvX, uvY);
+
+ ind++;
+ }
+ }
+
+ // - Triangles -
+
+ int[] triangles = new int[(segments * (segments + 1) * 4)];
+
+ for (int y = 0, t = 0; y < segments + 1; y++)
+ {
+ for (int x = 0; x < segments; x++, t += 4)
+ {
+ triangles[t + 0] = ((y + 0) * (segments + 1)) + x + 0;
+ triangles[t + 1] = ((y + 1) * (segments + 1)) + x + 0;
+ triangles[t + 2] = ((y + 1) * (segments + 1)) + x + 1;
+ triangles[t + 3] = ((y + 0) * (segments + 1)) + x + 1;
+ }
+ }
+
+ List polygons = new List();
+
+ int j = 0;
+ for (int i = 0; i < triangles.Length; i += 4)
+ {
+ Polygon p;
+ if (j < segments)
+ {
+ // top triangles.
+ polygons.Add(p = new Polygon(new Vertex[]
+ {
+ new Vertex(vertices[triangles[i+0]], Vector3.zero, uvs[triangles[i+0]]),
+ new Vertex(vertices[triangles[i+1]], Vector3.zero, uvs[triangles[i+1]]),
+ new Vertex(vertices[triangles[i+2]], Vector3.zero, uvs[triangles[i+2]]),
+ }, null, false, false));
+ }
+ else if (i >= triangles.Length - (segments * 4))
+ {
+ // bottom triangles.
+ polygons.Add(p = new Polygon(new Vertex[]
+ {
+ new Vertex(vertices[triangles[i+0]], Vector3.zero, uvs[triangles[i+0]]),
+ new Vertex(vertices[triangles[i+1]], Vector3.zero, uvs[triangles[i+1]]),
+ new Vertex(vertices[triangles[i+3]], Vector3.zero, uvs[triangles[i+3]]),
+ }, null, false, false));
+ }
+ else
+ {
+ // everything else is quads.
+ polygons.Add(p = new Polygon(new Vertex[]
+ {
+ new Vertex(vertices[triangles[i+0]], Vector3.zero, uvs[triangles[i+0]]),
+ new Vertex(vertices[triangles[i+1]], Vector3.zero, uvs[triangles[i+1]]),
+ new Vertex(vertices[triangles[i+2]], Vector3.zero, uvs[triangles[i+2]]),
+ new Vertex(vertices[triangles[i+3]], Vector3.zero, uvs[triangles[i+3]]),
+ }, null, false, false));
+ }
+ p.ResetVertexNormals();
+ j += 1;
+ }
+
+ return polygons.ToArray();
+ }
+
///
/// Generates the polygons from a supplied convex mesh, preserving quads if the MeshImporter has keepQuads set.
///
/// The polygons converted from the mesh.
/// Source mesh.
public static List GeneratePolygonsFromMesh(Mesh sourceMesh)
- {
- List generatedPolygons = new List();
- // Each sub mesh can have a different topology, i.e. triangles and quads
- for (int subMeshIndex = 0; subMeshIndex < sourceMesh.subMeshCount; subMeshIndex++)
- {
- MeshTopology meshTopology = sourceMesh.GetTopology(subMeshIndex);
- // The vertex count per polygon that we need to walk through the indices at
- int stride = 1;
- if(meshTopology == MeshTopology.Quads)
- {
- stride = 4;
- }
- else if(meshTopology == MeshTopology.Triangles)
- {
- stride = 3;
- }
- else
- {
- Debug.LogError("Unhandled sub mesh topology " + meshTopology + ". Ignoring sub mesh.");
- continue;
- }
-
- // Grab this sub mesh's index buffer
- int[] indices = sourceMesh.GetIndices(subMeshIndex);
-
- // Walk through the polygons in the index buffer
- for (int j = 0; j < indices.Length/stride; j++)
- {
- // Create a new vertex buffer for each polygon
- Vertex[] vertices = new Vertex[stride];
-
- // Pull out all the vertices for this source polygon
- for (int k = 0; k < stride; k++)
- {
- int vertexIndex = indices[j*stride+k];
-
- vertices[k] = new Vertex(sourceMesh.vertices[vertexIndex],
- sourceMesh.normals[vertexIndex],
- sourceMesh.uv[vertexIndex]);
- }
- // Generate a new polygon using these vertices and add it to the output polygon list
- Polygon polygon = new Polygon(vertices, null, false, false);
- generatedPolygons.Add(polygon);
- }
- }
- // Finally return all the converted polygons
- return generatedPolygons;
- }
-
- ///
- /// Generates a mesh from supplied polygons, particularly useful for visualising a brush's polygons on a MeshFilter
- ///
- /// Polygons.
- /// Mesh to be written to. Prior to settings the mesh buffers - if the existing mesh is null it will be set to a new one, otherwise the existing mesh is cleared
- /// Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex];
- public static void GenerateMeshFromPolygons(Polygon[] polygons, ref Mesh mesh, out List polygonIndices)
- {
- if(mesh == null)
- {
- mesh = new Mesh();
- }
- mesh.Clear();
- // mesh = new Mesh();
- List vertices = new List();
- List normals = new List();
- List uvs = new List();
- List colors = new List();
- List triangles = new List();
-
- // Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex];
- polygonIndices = new List();
-
- // Set up an indexer that tracks unique vertices, so that we reuse vertex data appropiately
- VertexList vertexList = new VertexList();
-
- // Iterate through every polygon and triangulate
- for (int i = 0; i < polygons.Length; i++)
- {
- Polygon polygon = polygons[i];
- List indices = new List();
-
- for (int j = 0; j < polygon.Vertices.Length; j++)
- {
- // Each vertex must know about its shared data for geometry tinting
- //polygon.Vertices[j].Shared = polygon.SharedBrushData;
- // If the vertex is already in the indexer, fetch the index otherwise add it and get the added index
- int index = vertexList.AddOrGet(polygon.Vertices[j]);
- // Put each vertex index in an array for use in the triangle generation
- indices.Add(index);
- }
-
- // Triangulate the n-sided polygon and allow vertex reuse by using indexed geometry
- for (int j = 2; j < indices.Count; j++)
- {
- triangles.Add(indices[0]);
- triangles.Add(indices[j - 1]);
- triangles.Add(indices[j]);
-
- // Map that this triangle is from the specified polygon (so we can map back from triangles to polygon)
- polygonIndices.Add(i);
- }
- }
-
- // Create the relevant buffers from the vertex array
- for (int i = 0; i < vertexList.Vertices.Count; i++)
- {
- vertices.Add(vertexList.Vertices[i].Position);
- normals.Add(vertexList.Vertices[i].Normal);
- uvs.Add(vertexList.Vertices[i].UV);
- // colors.Add(((SharedBrushData)indexer.Vertices[i].Shared).BrushTintColor);
- }
-
- // Set the mesh buffers
- mesh.vertices = vertices.ToArray();
- mesh.normals = normals.ToArray();
- mesh.colors = colors.ToArray();
- mesh.uv = uvs.ToArray();
- mesh.triangles = triangles.ToArray();
- }
+ {
+ List generatedPolygons = new List();
+ // Each sub mesh can have a different topology, i.e. triangles and quads
+ for (int subMeshIndex = 0; subMeshIndex < sourceMesh.subMeshCount; subMeshIndex++)
+ {
+ MeshTopology meshTopology = sourceMesh.GetTopology(subMeshIndex);
+ // The vertex count per polygon that we need to walk through the indices at
+ int stride = 1;
+ if (meshTopology == MeshTopology.Quads)
+ {
+ stride = 4;
+ }
+ else if (meshTopology == MeshTopology.Triangles)
+ {
+ stride = 3;
+ }
+ else
+ {
+ Debug.LogError("Unhandled sub mesh topology " + meshTopology + ". Ignoring sub mesh.");
+ continue;
+ }
+
+ // Grab this sub mesh's index buffer
+ int[] indices = sourceMesh.GetIndices(subMeshIndex);
+
+ // Walk through the polygons in the index buffer
+ for (int j = 0; j < indices.Length / stride; j++)
+ {
+ // Create a new vertex buffer for each polygon
+ Vertex[] vertices = new Vertex[stride];
+
+ // Pull out all the vertices for this source polygon
+ for (int k = 0; k < stride; k++)
+ {
+ int vertexIndex = indices[j * stride + k];
+
+ vertices[k] = new Vertex(sourceMesh.vertices[vertexIndex],
+ sourceMesh.normals[vertexIndex],
+ sourceMesh.uv[vertexIndex]);
+ }
+ // Generate a new polygon using these vertices and add it to the output polygon list
+ Polygon polygon = new Polygon(vertices, null, false, false);
+ generatedPolygons.Add(polygon);
+ }
+ }
+ // Finally return all the converted polygons
+ return generatedPolygons;
+ }
+
+ ///
+ /// Generates a mesh from supplied polygons, particularly useful for visualising a brush's polygons on a MeshFilter
+ ///
+ /// Polygons.
+ /// Mesh to be written to. Prior to settings the mesh buffers - if the existing mesh is null it will be set to a new one, otherwise the existing mesh is cleared
+ /// Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex];
+ public static void GenerateMeshFromPolygons(Polygon[] polygons, ref Mesh mesh, out List polygonIndices)
+ {
+ if (mesh == null)
+ {
+ mesh = new Mesh();
+ }
+ mesh.Clear();
+ // mesh = new Mesh();
+ List vertices = new List();
+ List normals = new List();
+ List uvs = new List();
+ List colors = new List();
+ List triangles = new List();
+
+ // Maps triangle index (input) to polygon index (output). i.e. int polyIndex = polygonIndices[triIndex];
+ polygonIndices = new List();
+
+ // Set up an indexer that tracks unique vertices, so that we reuse vertex data appropiately
+ VertexList vertexList = new VertexList();
+
+ // Iterate through every polygon and triangulate
+ for (int i = 0; i < polygons.Length; i++)
+ {
+ Polygon polygon = polygons[i];
+ List indices = new List();
+
+ for (int j = 0; j < polygon.Vertices.Length; j++)
+ {
+ // Each vertex must know about its shared data for geometry tinting
+ //polygon.Vertices[j].Shared = polygon.SharedBrushData;
+ // If the vertex is already in the indexer, fetch the index otherwise add it and get the added index
+ int index = vertexList.AddOrGet(polygon.Vertices[j]);
+ // Put each vertex index in an array for use in the triangle generation
+ indices.Add(index);
+ }
+
+ // Triangulate the n-sided polygon and allow vertex reuse by using indexed geometry
+ for (int j = 2; j < indices.Count; j++)
+ {
+ triangles.Add(indices[0]);
+ triangles.Add(indices[j - 1]);
+ triangles.Add(indices[j]);
+
+ // Map that this triangle is from the specified polygon (so we can map back from triangles to polygon)
+ polygonIndices.Add(i);
+ }
+ }
+
+ // Create the relevant buffers from the vertex array
+ for (int i = 0; i < vertexList.Vertices.Count; i++)
+ {
+ vertices.Add(vertexList.Vertices[i].Position);
+ normals.Add(vertexList.Vertices[i].Normal);
+ uvs.Add(vertexList.Vertices[i].UV);
+ // colors.Add(((SharedBrushData)indexer.Vertices[i].Shared).BrushTintColor);
+ }
+
+ // Set the mesh buffers
+ mesh.vertices = vertices.ToArray();
+ mesh.normals = normals.ToArray();
+ mesh.colors = colors.ToArray();
+ mesh.uv = uvs.ToArray();
+ mesh.triangles = triangles.ToArray();
+ }
///
/// Generates a mesh from the supplied polygons. particularly useful for visualising a
@@ -741,4 +884,5 @@ public static void GenerateMeshFromPolygonsFast(Polygon[] polygons, ref Mesh mes
}
}
}
+
#endif
\ No newline at end of file
diff --git a/Scripts/Importers/MaterialSearcher.cs b/Scripts/Importers/MaterialSearcher.cs
new file mode 100644
index 00000000..42cc7bb4
--- /dev/null
+++ b/Scripts/Importers/MaterialSearcher.cs
@@ -0,0 +1,66 @@
+#if UNITY_EDITOR || RUNTIME_CSG
+
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Sabresaurus.SabreCSG.Importers
+{
+ ///
+ /// Searches for materials by name more precisely to prevent false positives and caches them for
+ /// increasingly faster search results.
+ ///
+ public class MaterialSearcher
+ {
+ ///
+ /// The material cache dictionary.
+ ///
+ private Dictionary m_MaterialCache = new Dictionary();
+
+ ///
+ /// Attempts to find a material in the project by name.
+ ///
+ /// The material names to search for, longest to shortest like 'PlayrShp.Ceiling.Hullwk' and 'Hullwk'.
+ /// The material if found or null.
+ public Material FindMaterial(string[] names)
+ {
+#if UNITY_EDITOR
+ // iterate through all the names to search for, assuming it's longest to shortest.
+ for (int i = 0; i < names.Length; i++)
+ {
+ string name = names[i];
+ // check whether we already have this material in our cache.
+ if (m_MaterialCache.ContainsKey(name))
+ // if found immediately return this result.
+ return m_MaterialCache[name];
+
+ // have unity search for the asset (this can get very slow when there are a lot of materials in the project).
+ string[] guids = UnityEditor.AssetDatabase.FindAssets("t:Material " + name);
+
+ // iterate through unity's search results:
+ for (int j = 0; j < guids.Length; j++)
+ {
+ // get the file system path of the file.
+ string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guids[j]);
+ string file = System.IO.Path.GetFileNameWithoutExtension(path);
+
+ // make sure the file name matches the desired material name exactly to prevent false positives as much as possible.
+ if (file.Equals(name, StringComparison.InvariantCultureIgnoreCase))
+ {
+ // we found a perfect match, load the material.
+ Material material = UnityEditor.AssetDatabase.LoadAssetAtPath(path);
+ // put the material in the cache.
+ m_MaterialCache.Add(name, material);
+ // return the material.
+ return material;
+ }
+ }
+ }
+ // we didn't find anything...
+#endif
+ return null;
+ }
+ }
+}
+
+#endif
\ No newline at end of file
diff --git a/Scripts/Importers/MaterialSearcher.cs.meta b/Scripts/Importers/MaterialSearcher.cs.meta
new file mode 100644
index 00000000..982185c4
--- /dev/null
+++ b/Scripts/Importers/MaterialSearcher.cs.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: b1acbf10a759e5642b1519c9f7654505
+timeCreated: 1528227179
+licenseType: Free
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Scripts/Importers/UnrealGold/T3dImporter.cs b/Scripts/Importers/UnrealGold/T3dImporter.cs
index 2dfb9b83..f8cfb476 100644
--- a/Scripts/Importers/UnrealGold/T3dImporter.cs
+++ b/Scripts/Importers/UnrealGold/T3dImporter.cs
@@ -307,12 +307,16 @@ private bool TryParseProperty(string line, out string key, out object value)
if (rawvalue[1] == 'S' && rawvalue[2] != 'h')
{
// (Scale=(X=0.173205,Y=0.150000,Z=0.125000),SheerAxis=SHEER_ZX)
- string x = rawvalue.Replace("(Scale=", "x=").Replace(",SheerAxis=SHEER_ZX)", "");
+ string x = rawvalue.Replace("(Scale=", "X=").Replace(",SheerAxis=SHEER_ZX)", "");
string k;
object v;
if (TryParseProperty(x, out k, out v))
{
- value = v; // vector.
+ T3dVector3 vec = (T3dVector3)v;
+ vec.X = vec.X == 0.0f ? 1.0f : vec.X;
+ vec.Y = vec.Y == 0.0f ? 1.0f : vec.Y;
+ vec.Z = vec.Z == 0.0f ? 1.0f : vec.Z;
+ value = vec; // vector.
return true;
}
}
diff --git a/Scripts/Importers/UnrealGold/T3dMapConverter.cs b/Scripts/Importers/UnrealGold/T3dMapConverter.cs
index 1fed9f8f..2419d385 100644
--- a/Scripts/Importers/UnrealGold/T3dMapConverter.cs
+++ b/Scripts/Importers/UnrealGold/T3dMapConverter.cs
@@ -18,12 +18,15 @@ public static class T3dMapConverter
/// The model to import into.
/// The map to be imported.
/// The scale modifier.
- public static void Import(CSGModel model, T3dMap map, int scale = 64)
+ public static void Import(CSGModelBase model, T3dMap map, int scale = 64)
{
try
{
model.BeginUpdate();
+ // create a material searcher to associate materials automatically.
+ MaterialSearcher materialSearcher = new MaterialSearcher();
+
List brushes = map.Brushes;
Brush[] sabreBrushes = new Brush[brushes.Count];
@@ -43,12 +46,35 @@ public static void Import(CSGModel model, T3dMap map, int scale = 64)
T3dPolygon tpolygon = tbrush.Polygons[i];
// find the material in the unity project automatically.
- Material material = FindMaterial(tpolygon.Texture);
+ Material material;
+ if (tpolygon.Texture.Contains('.'))
+ {
+ // try finding both 'PlayrShp.Ceiling.Hullwk' and 'Hullwk'.
+ string tiny = tpolygon.Texture.Substring(tpolygon.Texture.LastIndexOf('.') + 1);
+ material = materialSearcher.FindMaterial(new string[] { tpolygon.Texture, tiny });
+ if (material == null)
+ Debug.Log("SabreCSG: Tried to find material '" + tpolygon.Texture + "' and also as '" + tiny + "' but it couldn't be found in the project.");
+ }
+ else
+ {
+ // only try finding 'Hullwk'.
+ material = materialSearcher.FindMaterial(new string[] { tpolygon.Texture });
+ if (material == null)
+ Debug.Log("SabreCSG: Tried to find material '" + tpolygon.Texture + "' but it couldn't be found in the project.");
+ }
Vertex[] vertices = new Vertex[tpolygon.Vertices.Count];
for (int j = 0; j < tpolygon.Vertices.Count; j++)
{
- vertices[j] = new Vertex(ToVector3(tpolygon.Vertices[j]) / (float)scale, ToVector3(tpolygon.Normal), GenerateUV(tpolygon, j, material));
+ // main-scale
+ // scale around pivot point.
+ Vector3 vertexPosition = ToVector3(tpolygon.Vertices[j]);
+ Vector3 pivot = ToVector3(tactor.PrePivot);
+ Vector3 difference = vertexPosition - pivot;
+ vertexPosition = difference.Multiply(ToVector3Raw(tactor.MainScale)) + pivot;
+
+ // post-scale
+ vertices[j] = new Vertex(vertexPosition.Multiply(ToVector3Raw(tactor.PostScale)) / (float)scale, ToVector3(tpolygon.Normal), GenerateUV(tpolygon, j, material));
}
// detect the polygon flags.
@@ -59,7 +85,7 @@ public static void Import(CSGModel model, T3dMap map, int scale = 64)
polygons[i] = new Polygon(vertices, material, false, userExcludeFromFinal);
}
- // position and rotate the brushes.
+ // position and rotate the brushes around their pivot point.
Transform transform = model.CreateCustomBrush(polygons).transform;
transform.position = (ToVector3(tactor.Location) / (float)scale) - (ToVector3(tactor.PrePivot) / (float)scale);
Vector3 axis;
@@ -126,6 +152,16 @@ private static Vector3 ToVector3(T3dVector3 vector3)
return new Vector3(-vector3.X, vector3.Z, vector3.Y);
}
+ ///
+ /// Converts to .
+ ///
+ /// The to be converted.
+ /// The .
+ private static Vector3 ToVector3Raw(T3dVector3 vector3)
+ {
+ return new Vector3(vector3.X, vector3.Z, vector3.Y);
+ }
+
private static float DotProduct(float ax, float ay, float az, float bx, float by, float bz)
{
return (ax * bx) + (ay * by) + (az * bz);
@@ -169,37 +205,6 @@ private static Quaternion T3dRotatorToQuaternion(T3dRotator rotator)
return quaternion;
}
-
- ///
- /// Attempts to find a material in the project by name.
- ///
- /// The material name to search for.
- /// The material if found or null.
- private static Material FindMaterial(string name)
- {
-#if UNITY_EDITOR
- // first try finding the fully qualified texture name like 'PlayrShp.Ceiling.Hullwk'.
- string texture = "";
- string guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + name).FirstOrDefault();
- if (guid == null)
- {
- // if it couldn't be found try a simplified name which UnrealEd typically exports like 'Hullwk'.
- texture = name;
- if (name.Contains('.'))
- texture = name.Substring(name.LastIndexOf('.') + 1);
- guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + texture).FirstOrDefault();
- }
- // if a material could be found using either option:
- if (guid != null)
- {
- // load the material.
- string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
- return UnityEditor.AssetDatabase.LoadAssetAtPath(path);
- }
- else { Debug.Log("SabreCSG: Tried to find material '" + name + "' and also as '" + texture + "' but it couldn't be found in the project."); }
-#endif
- return null;
- }
}
}
diff --git a/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs b/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs
index 72967cd3..afcfb4d8 100644
--- a/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs
+++ b/Scripts/Importers/ValveMapFormat2006/VmfWorldConverter.cs
@@ -12,18 +12,23 @@ namespace Sabresaurus.SabreCSG.Importers.ValveMapFormat2006
///
public static class VmfWorldConverter
{
+ private const float inchesInMeters = 0.03125f; // 1/32
+
///
/// Imports the specified world into the SabreCSG model.
///
/// The model to import into.
/// The world to be imported.
/// The scale modifier.
- public static void Import(CSGModel model, VmfWorld world, int scale = 32)
+ public static void Import(CSGModelBase model, VmfWorld world)
{
try
{
model.BeginUpdate();
+ // create a material searcher to associate materials automatically.
+ MaterialSearcher materialSearcher = new MaterialSearcher();
+
// group all the brushes together.
GroupBrush groupBrush = new GameObject("Source Engine Map").AddComponent();
groupBrush.transform.SetParent(model.transform);
@@ -49,7 +54,7 @@ public static void Import(CSGModel model, VmfWorld world, int scale = 32)
for (int j = solid.Sides.Count; j-- > 0;)
{
VmfSolidSide side = solid.Sides[j];
- Plane clip = new Plane(pr.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) / scale), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) / scale), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) / scale));
+ Plane clip = new Plane(pr.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) * inchesInMeters));
ClipUtility.ApplyClipPlane(pr, clip, false);
// find the polygons associated with the clipping plane.
@@ -63,8 +68,26 @@ public static void Import(CSGModel model, VmfWorld world, int scale = 32)
// detect collision-only brushes.
if (IsInvisibleMaterial(side.Material))
pr.IsVisible = false;
- // try finding the material in the project.
- polygon.Material = FindMaterial(side.Material);
+ // find the material in the unity project automatically.
+ Material material;
+ // try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'.
+ string materialName = side.Material.Replace("/", ".");
+ if (materialName.Contains('.'))
+ {
+ // try finding both 'BRICK.BRICKWALL052D' and 'BRICKWALL052D'.
+ string tiny = materialName.Substring(materialName.LastIndexOf('.') + 1);
+ material = materialSearcher.FindMaterial(new string[] { materialName, tiny });
+ if (material == null)
+ Debug.Log("SabreCSG: Tried to find material '" + materialName + "' and also as '" + tiny + "' but it couldn't be found in the project.");
+ }
+ else
+ {
+ // only try finding 'BRICKWALL052D'.
+ material = materialSearcher.FindMaterial(new string[] { materialName });
+ if (material == null)
+ Debug.Log("SabreCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project.");
+ }
+ polygon.Material = material;
// calculate the texture coordinates.
int w = 256;
int h = 256;
@@ -73,7 +96,7 @@ public static void Import(CSGModel model, VmfWorld world, int scale = 32)
w = polygon.Material.mainTexture.width;
h = polygon.Material.mainTexture.height;
}
- CalculateTextureCoordinates(polygon, w, h, side.UAxis, side.VAxis, scale);
+ CalculateTextureCoordinates(pr, polygon, w, h, side.UAxis, side.VAxis);
}
}
@@ -129,7 +152,7 @@ public static void Import(CSGModel model, VmfWorld world, int scale = 32)
for (int j = solid.Sides.Count; j-- > 0;)
{
VmfSolidSide side = solid.Sides[j];
- Plane clip = new Plane(pr.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) / scale), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) / scale), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) / scale));
+ Plane clip = new Plane(pr.transform.InverseTransformPoint(new Vector3(side.Plane.P1.X, side.Plane.P1.Z, side.Plane.P1.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P2.X, side.Plane.P2.Z, side.Plane.P2.Y) * inchesInMeters), pr.transform.InverseTransformPoint(new Vector3(side.Plane.P3.X, side.Plane.P3.Z, side.Plane.P3.Y) * inchesInMeters));
ClipUtility.ApplyClipPlane(pr, clip, false);
// find the polygons associated with the clipping plane.
@@ -143,8 +166,26 @@ public static void Import(CSGModel model, VmfWorld world, int scale = 32)
// detect collision-only brushes.
if (IsInvisibleMaterial(side.Material))
pr.IsVisible = false;
- // try finding the material in the project.
- polygon.Material = FindMaterial(side.Material);
+ // find the material in the unity project automatically.
+ Material material;
+ // try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'.
+ string materialName = side.Material.Replace("/", ".");
+ if (materialName.Contains('.'))
+ {
+ // try finding both 'BRICK.BRICKWALL052D' and 'BRICKWALL052D'.
+ string tiny = materialName.Substring(materialName.LastIndexOf('.') + 1);
+ material = materialSearcher.FindMaterial(new string[] { materialName, tiny });
+ if (material == null)
+ Debug.Log("SabreCSG: Tried to find material '" + materialName + "' and also as '" + tiny + "' but it couldn't be found in the project.");
+ }
+ else
+ {
+ // only try finding 'BRICKWALL052D'.
+ material = materialSearcher.FindMaterial(new string[] { materialName });
+ if (material == null)
+ Debug.Log("SabreCSG: Tried to find material '" + materialName + "' but it couldn't be found in the project.");
+ }
+ polygon.Material = material;
// calculate the texture coordinates.
int w = 256;
int h = 256;
@@ -153,7 +194,7 @@ public static void Import(CSGModel model, VmfWorld world, int scale = 32)
w = polygon.Material.mainTexture.width;
h = polygon.Material.mainTexture.height;
}
- CalculateTextureCoordinates(polygon, w, h, side.UAxis, side.VAxis, scale);
+ CalculateTextureCoordinates(pr, polygon, w, h, side.UAxis, side.VAxis);
}
}
@@ -183,27 +224,32 @@ public static void Import(CSGModel model, VmfWorld world, int scale = 32)
}
}
- // shoutouts to Stefan Hajnoczi for your map importer giving me a clue on how to do this.
- private static void CalculateTextureCoordinates(Polygon polygon, int textureWidth, int textureHeight, VmfAxis UAxis, VmfAxis VAxis, int scale)
+ // shoutouts to Aleksi Juvani for your vmf importer giving me a clue on why my textures were misaligned.
+ // had to add the world space position of the brush to the calculations! https://github.com/aleksijuvani
+ private static void CalculateTextureCoordinates(PrimitiveBrush pr, Polygon polygon, int textureWidth, int textureHeight, VmfAxis UAxis, VmfAxis VAxis)
{
+ UAxis.Translation = UAxis.Translation % textureWidth;
+ VAxis.Translation = VAxis.Translation % textureHeight;
+
+ if (UAxis.Translation < -textureWidth / 2f)
+ UAxis.Translation += textureWidth;
+
+ if (VAxis.Translation < -textureHeight / 2f)
+ VAxis.Translation += textureHeight;
+
// calculate texture coordinates.
for (int i = 0; i < polygon.Vertices.Length; i++)
{
- float U, V;
+ var vertex = pr.transform.position + polygon.Vertices[i].Position;
- Plane uplane = new Plane(new Vector3(UAxis.Vector.X, UAxis.Vector.Z, UAxis.Vector.Y), UAxis.Translation);
- Plane vplane = new Plane(new Vector3(VAxis.Vector.X, VAxis.Vector.Z, VAxis.Vector.Y), VAxis.Translation);
+ Vector3 uaxis = new Vector3(UAxis.Vector.X, UAxis.Vector.Z, UAxis.Vector.Y);
+ Vector3 vaxis = new Vector3(VAxis.Vector.X, VAxis.Vector.Z, VAxis.Vector.Y);
- U = Vector3.Dot(uplane.normal, polygon.Vertices[i].Position);
- U = (U / textureWidth) / (UAxis.Scale / scale);
- U = U + (UAxis.Translation / textureWidth);
+ var u = Vector3.Dot(vertex, uaxis) / (textureWidth * (UAxis.Scale * inchesInMeters)) + UAxis.Translation / textureWidth;
+ var v = Vector3.Dot(vertex, vaxis) / (textureHeight * (VAxis.Scale * inchesInMeters)) + VAxis.Translation / textureHeight;
- V = Vector3.Dot(vplane.normal, polygon.Vertices[i].Position);
- V = (V / textureHeight) / (VAxis.Scale / scale);
- V = V + (VAxis.Translation / textureHeight);
-
- polygon.Vertices[i].UV.x = U;
- polygon.Vertices[i].UV.y = 1.0f - V;
+ polygon.Vertices[i].UV.x = u;
+ polygon.Vertices[i].UV.y = -v;
}
}
@@ -276,38 +322,6 @@ private static bool IsSpecialMaterial(string name)
}
return false;
}
-
- ///
- /// Attempts to find a material in the project by name.
- ///
- /// The material name to search for.
- /// The material if found or null.
- private static Material FindMaterial(string name)
- {
-#if UNITY_EDITOR
- // first try finding the fully qualified texture name with '/' replaced by '.' so 'BRICK.BRICKWALL052D'.
- name = name.Replace("/", ".");
- string texture = "";
- string guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + name).FirstOrDefault();
- if (guid == null)
- {
- // if it couldn't be found try a simplified name like 'BRICKWALL052D'.
- texture = name;
- if (name.Contains('.'))
- texture = name.Substring(name.LastIndexOf('.') + 1);
- guid = UnityEditor.AssetDatabase.FindAssets("t:Material " + texture).FirstOrDefault();
- }
- // if a material could be found using either option:
- if (guid != null)
- {
- // load the material.
- string path = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
- return UnityEditor.AssetDatabase.LoadAssetAtPath(path);
- }
- else { Debug.Log("SabreCSG: Tried to find material '" + name + "' and also as '" + texture + "' but it couldn't be found in the project."); }
-#endif
- return null;
- }
}
}
diff --git a/Scripts/Tools/DrawEditor.cs b/Scripts/Tools/DrawEditor.cs
index 44b846ab..5559f7fa 100644
--- a/Scripts/Tools/DrawEditor.cs
+++ b/Scripts/Tools/DrawEditor.cs
@@ -945,7 +945,7 @@ private void OnRepaint(SceneView sceneView, Event e)
{
GL.Color(Color.blue);
}
- else
+ else if (csgMode == CSGMode.Subtract)
{
GL.Color(Color.yellow);
}
diff --git a/Scripts/Tools/SurfaceEditor.cs b/Scripts/Tools/SurfaceEditor.cs
index b8a77170..2e1bc4b5 100755
--- a/Scripts/Tools/SurfaceEditor.cs
+++ b/Scripts/Tools/SurfaceEditor.cs
@@ -1016,7 +1016,7 @@ private void OnRepaint(SceneView sceneView, Event e)
Vector3 worldCenterPoint = brushTransform.TransformPoint(currentPolygon.GetCenterPoint());
// Offset the gizmo so it's very slightly above the polygon, to avoid depth fighting
- if (brush.Mode == CSGMode.Add)
+ if (brush.Mode == CSGMode.Add || brush.Mode == CSGMode.Volume)
{
worldCenterPoint += normal * 0.02f;
}
@@ -1072,7 +1072,7 @@ private void OnRepaint(SceneView sceneView, Event e)
toDeselect.Add(polygon);
}
}
- else
+ else if (brush.Mode == CSGMode.Subtract)
{
// is the camera on the positive side of the plane?
if (Vector3.Dot(brush.transform.TransformDirection(polygon.Plane.normal), Camera.current.transform.position - brush.transform.TransformPoint(polygon.GetCenterPoint())) < 0)
diff --git a/Scripts/Tools/Tool.cs b/Scripts/Tools/Tool.cs
index 206bb5db..5a923c60 100644
--- a/Scripts/Tools/Tool.cs
+++ b/Scripts/Tools/Tool.cs
@@ -39,10 +39,15 @@ public void SetSelection(GameObject selectedGameObject, GameObject[] selectedGam
{
BrushBase matchedBrushBase = Selection.gameObjects[i].GetComponent();
- // If we've selected a brush base that isn't a prefab in the project
+ // If we've selected a brush base that isn't a prefab in the project
+
if(matchedBrushBase != null
- && !(PrefabUtility.GetPrefabParent(matchedBrushBase.gameObject) == null
- && PrefabUtility.GetPrefabObject(matchedBrushBase.transform) != null))
+#if UNITY_2018_2
+ && !(PrefabUtility.GetCorrespondingObjectFromSource(matchedBrushBase.gameObject) == null
+#else
+ && !(PrefabUtility.GetPrefabParent(matchedBrushBase.gameObject) == null
+#endif
+ && PrefabUtility.GetPrefabObject(matchedBrushBase.transform) != null))
{
brushBases.Add(matchedBrushBase);
}
@@ -55,17 +60,21 @@ public void SetSelection(GameObject selectedGameObject, GameObject[] selectedGam
BrushBase newPrimaryBrushBase = null;
PrimitiveBrush newPrimaryBrush = null;
- // Make sure it's not null and that it isn't a prefab in the project
- if(selectedGameObject != null
+ // Make sure it's not null and that it isn't a prefab in the project
+ if(selectedGameObject != null
+#if UNITY_2018_2
+ && !(PrefabUtility.GetCorrespondingObjectFromSource(selectedGameObject) == null
+#else
&& !(PrefabUtility.GetPrefabParent(selectedGameObject) == null
- && PrefabUtility.GetPrefabObject(selectedGameObject.transform) != null))
- {
- newPrimaryBrushBase = selectedGameObject.GetComponent();// brushBases.FirstOrDefault();
- newPrimaryBrush = selectedGameObject.GetComponent();// primitiveBrushes.Where(item => item != null).FirstOrDefault();
- }
-
- // If the primary brush base has changed
- if (primaryTargetBrushBase != newPrimaryBrushBase
+#endif
+ && PrefabUtility.GetPrefabObject(selectedGameObject.transform) != null))
+ {
+ newPrimaryBrushBase = selectedGameObject.GetComponent();// brushBases.FirstOrDefault();
+ newPrimaryBrush = selectedGameObject.GetComponent();// primitiveBrushes.Where(item => item != null).FirstOrDefault();
+ }
+
+ // If the primary brush base has changed
+ if (primaryTargetBrushBase != newPrimaryBrushBase
|| (primaryTargetBrushBase == null && newPrimaryBrushBase != null)) // Special case for undo where references are equal but one is null
{
primaryTargetBrushBase = newPrimaryBrushBase;
@@ -269,4 +278,4 @@ public virtual bool PreventDragDrop
}
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Scripts/UI/MaterialPaletteWindow.cs b/Scripts/UI/MaterialPaletteWindow.cs
index 3d2b1ae8..ba14a655 100644
--- a/Scripts/UI/MaterialPaletteWindow.cs
+++ b/Scripts/UI/MaterialPaletteWindow.cs
@@ -13,6 +13,8 @@ static void CreateAndShow()
{
EditorWindow window = EditorWindow.GetWindow("Material Palette");//false, "Palette", true);
+ window.minSize = new Vector2( 120, 120 );
+
window.Show();
}
diff --git a/Scripts/UI/PaletteWindow.cs b/Scripts/UI/PaletteWindow.cs
index 5ff878a2..fcd130ff 100644
--- a/Scripts/UI/PaletteWindow.cs
+++ b/Scripts/UI/PaletteWindow.cs
@@ -70,6 +70,8 @@ static void CreateAndShow()
{
EditorWindow window = EditorWindow.GetWindow("Palette");//false, "Palette", true);
+ window.minSize = new Vector2( 120, 120 );
+
window.Show();
}
diff --git a/Scripts/UI/SabreCSGPreferences.cs b/Scripts/UI/SabreCSGPreferences.cs
index c1b78473..8631c106 100644
--- a/Scripts/UI/SabreCSGPreferences.cs
+++ b/Scripts/UI/SabreCSGPreferences.cs
@@ -14,7 +14,7 @@ public class SabreCSGPreferences : EditorWindow
private const string RUNTIME_CSG_DEFINE = "RUNTIME_CSG";
private static readonly Vector2 WINDOW_SIZE = new Vector2(370, 360);
- private static Event cachedEvent;
+ //private static Event cachedEvent;
public static void CreateAndShow()
{
@@ -94,6 +94,21 @@ public static void PreferencesGUI()
SceneView.RepaintAll();
}
+ EditorGUILayout.Space();
+ EditorGUI.indentLevel = 1;
+ EditorGUILayout.LabelField("Developer Options", EditorStyles.boldLabel);
+ EditorGUI.indentLevel = 0;
+ EditorGUILayout.Space();
+
+ EditorGUI.BeginChangeCheck();
+ CurrentSettings.ShowHiddenGameObjectsInHierarchy = GUILayout.Toggle(CurrentSettings.ShowHiddenGameObjectsInHierarchy, "Show hidden game objects in hierarchy");
+ if (EditorGUI.EndChangeCheck())
+ {
+ // What's shown in the SceneView has potentially changed, so force it to repaint
+ CSGModel.RebuildAllVolumes();
+ SceneView.RepaintAll();
+ }
+
GUILayout.Space(10);
if (GUILayout.Button("Change key mappings"))
diff --git a/Scripts/UI/SabreCSGResources.cs b/Scripts/UI/SabreCSGResources.cs
index 23b4f138..96eea43e 100644
--- a/Scripts/UI/SabreCSGResources.cs
+++ b/Scripts/UI/SabreCSGResources.cs
@@ -41,7 +41,7 @@ public static class SabreCSGResources
///
/// Path local to the SabreCSG folder
///
- private static Object LoadObject(string sabrePath)
+ public static Object LoadObject(string sabrePath)
{
bool found = false;
@@ -93,6 +93,8 @@ public static Texture2D GetButtonTexture(PrimitiveBrushType brushType)
return ButtonSphereTexture;
else if (brushType == PrimitiveBrushType.Cone)
return ButtonConeTexture;
+ else if (brushType == PrimitiveBrushType.Capsule)
+ return ButtonCapsuleTexture;
else
return ButtonCubeTexture;
}
@@ -175,6 +177,14 @@ public static Texture2D SubtractIconTexture
}
}
+ public static Texture2D VolumeIconTexture
+ {
+ get
+ {
+ return (Texture2D)LoadObject("Gizmos/Volume.png");
+ }
+ }
+
public static Texture2D NoCSGIconTexture
{
get
@@ -191,6 +201,14 @@ public static Texture2D GroupIconTexture
}
}
+ public static Texture2D CollisionIconTexture
+ {
+ get
+ {
+ return (Texture2D)LoadObject("Gizmos/Collision.png");
+ }
+ }
+
public static Texture2D DialogOverlayTexture
{
get
@@ -263,6 +281,14 @@ public static Texture2D ButtonConeTexture
}
}
+ public static Texture2D ButtonCapsuleTexture
+ {
+ get
+ {
+ return (Texture2D)LoadObject("Gizmos/ButtonCapsule.png");
+ }
+ }
+
public static Texture2D ButtonCurvedStairsTexture
{
get
@@ -383,6 +409,14 @@ public static Texture2D ShapeEditorOpenTexture
}
}
+ public static Texture2D ShapeEditorRestoreTexture
+ {
+ get
+ {
+ return (Texture2D)LoadObject("Gizmos/ShapeEditorRestore.png");
+ }
+ }
+
public static Texture2D ShapeEditorRotate90LeftTexture
{
get
@@ -439,6 +473,14 @@ public static Texture2D ShapeEditorSegmentInsertTexture
}
}
+ public static Texture2D ShapeEditorSegmentExtrudeTexture
+ {
+ get
+ {
+ return (Texture2D)LoadObject("Gizmos/ShapeEditorSegmentExtrude.png");
+ }
+ }
+
public static Texture2D ShapeEditorSegmentLinearTexture
{
get
@@ -632,6 +674,16 @@ public static Material GetSubtractMaterial()
return (Material)LoadObject("Materials/Subtract.mat");
}
+ public static Material GetVolumeMaterial()
+ {
+ return (Material)LoadObject("Materials/Volume.mat");
+ }
+
+ public static Material GetCollisionMaterial()
+ {
+ return (Material)LoadObject("Materials/Collision.mat");
+ }
+
public static Material GetPlaneMaterial()
{
if (planeMaterial == null)
diff --git a/Scripts/UI/SabreGUILayout.cs b/Scripts/UI/SabreGUILayout.cs
index 14af876c..67f11b4a 100644
--- a/Scripts/UI/SabreGUILayout.cs
+++ b/Scripts/UI/SabreGUILayout.cs
@@ -365,6 +365,30 @@ public static void DrawOutlinedBox(Rect rect, Color fillColor)
GUI.color = fillColor;
GUI.DrawTexture(rect, EditorGUIUtility.whiteTexture);
}
+
+ ///
+ /// Displays a field for layer masks.
+ ///
+ /// The label to show.
+ /// The current mask.
+ /// The new mask.
+ public static int LayerMaskField(GUIContent label, int mask)
+ {
+ System.Collections.Generic.List layerNames = new System.Collections.Generic.List();
+ for (int i = 0; i < 32; i++)
+ {
+ string layerName = UnityEditorInternal.InternalEditorUtility.GetLayerName(i);
+ //if (!(layerName == string.Empty))
+ //{
+ layerNames.Add(layerName);
+ //}
+ }
+
+ // this currently shows a lot of empty entries.
+ // we should figure out which ones are empty, not display those and fix the layer mask accordingly.
+
+ return EditorGUILayout.MaskField(label, mask, layerNames.ToArray());
+ }
}
}
#endif
\ No newline at end of file
diff --git a/Scripts/UI/Toolbar.cs b/Scripts/UI/Toolbar.cs
index 4a636d8d..b9f0a31e 100644
--- a/Scripts/UI/Toolbar.cs
+++ b/Scripts/UI/Toolbar.cs
@@ -219,7 +219,7 @@ static void CreateCompoundBrush(object compoundBrushType)
{
// Make sure we're actually being asked to create a compound brush
if(compoundBrushType != null
- && compoundBrushType == typeof(Type)
+ && compoundBrushType is Type
&& !typeof(CompoundBrush).IsAssignableFrom((Type)compoundBrushType))
{
throw new ArgumentException("Specified type must be derived from CompoundBrush");
@@ -575,4 +575,4 @@ private static void OnBottomToolbarGUI(int windowID)
}
}
}
-#endif
\ No newline at end of file
+#endif
diff --git a/Shaders/ShapeEditorGrid.shader b/Shaders/ShapeEditorGrid.shader
index 6b5db1eb..64d599d0 100644
--- a/Shaders/ShapeEditorGrid.shader
+++ b/Shaders/ShapeEditorGrid.shader
@@ -6,7 +6,8 @@
_OffsetY ("Offset Y", Float) = 0.0
_ScrollX ("Scroll X", Float) = 0.0
_ScrollY ("Scroll Y", Float) = 0.0
- _Zoom ("Zoom", Float) = 16.0
+ _Zoom ("Zoom", Float) = 16.0
+ _PixelsPerPoint ("Pixels Per Point", Float) = 1.0
_Background ("Background", 2D) = "white" { }
}
@@ -50,20 +51,30 @@
float _ScrollX;
float _ScrollY;
float _Zoom;
- float4 _Background_TexelSize;
- sampler2D _Background;
+ float _PixelsPerPoint;
+ float4 _Background_TexelSize;
+ sampler2D _Background;
+ float _Height;
// fragment shader
fixed4 frag(v2f IN) : SV_Target
{
// calculate grid offset and scrolling.
- float4 pos = IN.pos;
- pos.x -= _OffsetX + _ScrollX;
- pos.y -= _OffsetY + _ScrollY;
+ float4 pos = IN.pos;
+#if UNITY_UV_STARTS_AT_TOP
+ pos.y -= _PixelsPerPoint * _OffsetY;
+#else
+ pos.y = _Height - pos.y;
+ pos.y += _PixelsPerPoint * _OffsetY;
+#endif
+ pos /= _PixelsPerPoint;
+
+ pos.x -= _OffsetX + _ScrollX;
+ pos.y -= _ScrollY;
// the 1x1 grid line light gray color.
fixed4 col;
- col = fixed4(0.922f, 0.922f, 0.922f, 1.0f);
+ col = fixed4(0.922f, 0.922f, 0.922f, 1.0f);
// calculate zoom.
float s8 = (_Zoom * 8);
@@ -97,7 +108,7 @@
fixed2 offset = fixed2(pos.x + size.x / 2.0f, pos.y + size.y / 2.0f);
if (offset.x > 0 && offset.y > 0 && offset.x < size.x && offset.y < size.y)
col = tex2D(_Background, fixed2(offset.x / size.x, offset.y / -size.y)).rgba;
-
+
return col;
}
ENDCG
diff --git a/Shaders/ShapeEditorLine.shader b/Shaders/ShapeEditorLine.shader
index 2535a68d..499dcbfa 100644
--- a/Shaders/ShapeEditorLine.shader
+++ b/Shaders/ShapeEditorLine.shader
@@ -1,5 +1,10 @@
Shader "SabreCSG/ShapeEditorLine"
{
+ Properties
+ {
+ _CutoffY ("Cutoff Y", Float) = 0.0
+ }
+
SubShader
{
Pass
@@ -29,6 +34,9 @@
float4 pos : SV_POSITION;
};
+ float _CutoffY;
+ float _Height;
+
// vertex shader
v2f vert(appdata IN)
{
@@ -47,8 +55,14 @@
{
fixed4 col;
col = IN.color;
- if (IN.pos.y < 35)
- discard;
+#if UNITY_UV_STARTS_AT_TOP
+ if (IN.pos.y < _CutoffY)
+ discard;
+#else
+ if (IN.pos.y > _Height)
+ discard;
+#endif
+
return col;
}
ENDCG