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