From 77dcf1faf95297015f2d685b8a758e27f958a018 Mon Sep 17 00:00:00 2001 From: mitchellhansen Date: Sun, 3 Jan 2021 22:41:33 -0800 Subject: [PATCH] ripping up the example --- Cargo.toml | 1 + resources/Tree_01.mtl | 14 + resources/Tree_01.mtl.meta | 7 + resources/Tree_01.obj | 229 ++++ resources/Tree_01.obj.meta | 102 ++ resources/Tree_02.mtl | 14 + resources/Tree_02.mtl.meta | 7 + resources/Tree_02.obj | 229 ++++ resources/Tree_02.obj.meta | 102 ++ resources/bake.frag.spv | Bin 180 -> 252 bytes resources/bake.vert | 2 +- resources/bake.vert.spv | Bin 1312 -> 1460 bytes resources/cube.obj | 43 + resources/forward.frag.spv | Bin 4612 -> 4684 bytes resources/forward.vert | 4 +- resources/forward.vert.spv | Bin 1948 -> 2148 bytes resources/monkey.mtl | 10 + resources/monkey.obj | 2423 ++++++++++++++++++++++++++++++++++++ resources/my_tree.mtl | 10 + resources/my_tree.obj | 621 +++++++++ src/framework.rs | 275 +--- src/geometry.rs | 87 ++ src/light.rs | 49 + src/main.rs | 1064 ++++------------ src/render.rs | 551 ++++++++ src/runtime.rs | 202 +++ 26 files changed, 4974 insertions(+), 1072 deletions(-) create mode 100755 resources/Tree_01.mtl create mode 100644 resources/Tree_01.mtl.meta create mode 100755 resources/Tree_01.obj create mode 100644 resources/Tree_01.obj.meta create mode 100755 resources/Tree_02.mtl create mode 100644 resources/Tree_02.mtl.meta create mode 100755 resources/Tree_02.obj create mode 100644 resources/Tree_02.obj.meta create mode 100644 resources/cube.obj create mode 100644 resources/monkey.mtl create mode 100644 resources/monkey.obj create mode 100644 resources/my_tree.mtl create mode 100644 resources/my_tree.obj create mode 100644 src/geometry.rs create mode 100644 src/light.rs create mode 100644 src/render.rs create mode 100644 src/runtime.rs diff --git a/Cargo.toml b/Cargo.toml index 9123716..fb7b02c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ bytemuck = "1" noise = "0.6" ddsfile = "0.4" wgpu-subscriber = "0.1.0" +tobj = "2.0.3" diff --git a/resources/Tree_01.mtl b/resources/Tree_01.mtl new file mode 100755 index 0000000..954f31b --- /dev/null +++ b/resources/Tree_01.mtl @@ -0,0 +1,14 @@ +# +## Alias OBJ Material File +# Exported from SketchUp, (c) 2000-2012 Trimble Navigation Limited + +newmtl Leafs +Ka 0.000000 0.000000 0.000000 +Kd 0.270588 0.407843 0.400000 +Ks 0.330000 0.330000 0.330000 + +newmtl Wood +Ka 0.000000 0.000000 0.000000 +Kd 0.666667 0.545098 0.356863 +Ks 0.330000 0.330000 0.330000 + diff --git a/resources/Tree_01.mtl.meta b/resources/Tree_01.mtl.meta new file mode 100644 index 0000000..1fc7185 --- /dev/null +++ b/resources/Tree_01.mtl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: b6f6049ee83f1b2a99865cbd2ad3eef7 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/resources/Tree_01.obj b/resources/Tree_01.obj new file mode 100755 index 0000000..5dddfea --- /dev/null +++ b/resources/Tree_01.obj @@ -0,0 +1,229 @@ +# Alias OBJ Model File +# Exported from SketchUp, (c) 2000-2012 Trimble Navigation Limited +# File units = meters + +mtllib Tree_01.mtl + +g Mesh1 Tree Model + +usemtl Leafs +v 0 0 -1.2957 +vt -3.67031e-018 -0.258226 +vn -5.60096e-018 0.438447 -0.898757 +v 0.177 0.6 -1.003 +vt -0.0804545 0.0452234 +v 1.003 0.6 -1.003 +vt -0.455909 0.0452234 +v 1.18 0 -1.2957 +vt -0.536364 -0.258226 +f 1/1/1 2/2/1 3/3/1 4/4/1 + +v 0.177 0.6 -0.177 +vt -0.0804545 0.284347 +vn -0.959136 0.282945 7.971e-018 +vt -0.455909 0.284347 +vt -0.588956 1.3849e-018 +v 0 0 -0 +vt 0 0 +f 5/5/2 2/6/2 1/7/2 6/8/2 + +vt -0.0804545 0.0804545 +vn 0 -1 -0 +v -0.0969352 0.6 -0.0826 +vt 0.0440615 0.0375455 +vt -0.0804545 0.455909 +f 5/9/3 7/10/3 2/11/3 + +v 1.0974 0.6 -0.0826 +vt -0.498818 0.0375455 +f 7/10/3 5/9/3 8/12/3 + +v 1.003 0.6 -0.177 +vt -0.455909 0.0804545 +f 8/12/3 5/9/3 9/13/3 + +v 1.18 0 -0 +vt 0.536364 -1.26083e-018 +vn 7.96848e-018 0.282945 0.959136 +vt 0.455909 0.284347 +vt 0.0804545 0.284347 +f 10/14/4 9/15/4 5/16/4 6/8/4 + +vt 8.91502e-019 -0.151761 +vn 0.959136 0.282945 1.5942e-018 +vt 0.588956 -0.151761 +vt 0.455909 0.132585 +vt 0.0804545 0.132585 +f 10/17/5 4/18/5 3/19/5 9/20/5 + +vt -0.536364 0 +v 0.786697 0 -0.393303 +vt -0.35759 0.178774 +vt -0.536364 0.588956 +f 10/21/3 11/22/3 4/23/3 + +v 0.393303 0 -0.393303 +vt -0.178774 0.178774 +f 10/21/3 12/24/3 11/22/3 + +f 6/8/3 12/24/3 10/21/3 + +v 0.393303 0 -0.786697 +vt -0.178774 0.35759 +f 12/24/3 6/8/3 13/25/3 + +vt 0 0.588956 +f 1/26/3 13/25/3 6/8/3 + +f 13/25/3 1/26/3 4/23/3 + +v 0.786697 0 -0.786697 +vt -0.35759 0.35759 +f 13/25/3 4/23/3 14/27/3 + +f 14/27/3 4/23/3 11/22/3 + +usemtl Wood +v 0.837838 -0.495868 -0.342162 +vt 0.155528 -0.263275 +vn 0.994724 0.102591 6.0893e-019 +v 0.837838 -0.495868 -0.837838 +vt 0.380836 -0.263275 +vt 0.35759 -0.0366853 +vt 0.178774 -0.0366853 +f 15/28/6 16/29/6 14/30/6 11/31/6 + +v 0.342162 -0.495868 -0.342162 +vt -0.155528 0.155528 +v 0.342162 -0.495868 -0.837838 +vt -0.155528 0.380836 +vt -0.380836 0.380836 +vt -0.380836 0.155528 +f 17/32/3 18/33/3 16/34/3 15/35/3 + +vt -0.178774 0.0183405 +vn -0.994724 0.102591 -5.80807e-018 +vt -0.35759 0.0183405 +vt -0.380836 -0.208249 +vt -0.155528 -0.208249 +f 12/36/7 13/37/7 18/38/7 17/39/7 + +usemtl Leafs +f 12/24/3 13/25/3 14/27/3 11/22/3 + +usemtl Wood +vt -0.155528 -0.263275 +vn 9.37084e-019 0.102591 -0.994724 +vt -0.178774 -0.0366853 +vt -0.35759 -0.0366853 +vt -0.380836 -0.263275 +f 18/40/8 13/41/8 14/42/8 16/43/8 + +vt 0.380836 -0.208249 +vn 7.35408e-018 0.102591 0.994724 +vt 0.35759 0.0183405 +vt 0.178774 0.0183405 +vt 0.155528 -0.208249 +f 15/44/9 11/45/9 12/46/9 17/47/9 + +usemtl Leafs +vt -0.455909 0.455909 +v 1.0974 0.6 -1.0974 +vt -0.498818 0.498818 +f 3/48/3 19/49/3 9/13/3 + +f 2/11/3 19/49/3 3/48/3 + +v -0.0969352 0.6 -1.0974 +vt 0.0440615 0.498818 +f 2/11/3 20/50/3 19/49/3 + +f 20/50/3 2/11/3 7/10/3 + +v 0.23482 1.34304 -0.23482 +vt -0.106736 0.60095 +vn -0.913119 0.407693 2.16966e-017 +v 0.23482 1.34304 -0.94518 +vt -0.429627 0.60095 +vt -0.498818 0.231069 +vt -0.0375455 0.231069 +f 21/51/10 22/52/10 20/53/10 7/54/10 + +vt -0.106736 0.106736 +v 0.153636 1.34304 -0.153636 +vt -0.0698345 0.0698345 +vt -0.106736 0.429627 +f 21/55/3 23/56/3 22/57/3 + +v 1.14207 1.34304 -0.153636 +vt -0.519121 0.0698345 +f 23/56/3 21/55/3 24/58/3 + +v 0.94518 1.34304 -0.23482 +vt -0.429627 0.106736 +f 24/58/3 21/55/3 25/59/3 + +vt 0.498818 0.274714 +vn 5.19277e-018 0.200693 0.979654 +vt 0.429627 0.619473 +vt 0.106736 0.619473 +vt -0.0440615 0.274714 +f 8/60/11 25/61/11 21/62/11 7/63/11 + +vt 0.0375455 0.167069 +vn 0.979654 0.200693 -1.44372e-018 +vt 0.498818 0.167069 +v 0.94518 1.34304 -0.94518 +vt 0.429627 0.511829 +vt 0.106736 0.511829 +f 8/64/12 19/65/12 26/66/12 25/67/12 + +f 8/12/3 9/13/3 19/49/3 + +vt 0.0440615 0.167069 +vn -2.59638e-018 0.200693 -0.979654 +vt -0.106736 0.511829 +vt -0.429627 0.511829 +vt -0.498818 0.167069 +f 20/68/13 22/69/13 26/70/13 19/71/13 + +v 1.14207 1.34304 -1.02636 +vt -0.519121 0.466529 +vt -0.429627 0.429627 +f 22/57/3 27/72/3 26/73/3 + +v 0.153636 1.34304 -1.02636 +vt -0.0698345 0.466529 +f 22/57/3 28/74/3 27/72/3 + +f 28/74/3 22/57/3 23/56/3 + +v 0.383388 3.21676 -0.59 +vt -0.268182 1.4725 +vn -0.992566 0.121707 7.00107e-018 +vt -0.466529 0.614434 +vt -0.0698345 0.614434 +f 29/75/14 28/76/14 23/77/14 + +vt -0.174267 1.36323 +vn 1.92555e-017 0.226817 -0.973937 +vt -0.519121 0.488746 +vt -0.0698345 0.488746 +f 29/78/15 27/79/15 28/80/15 + +vt 0.466529 0.371018 +vn 0.926901 0.375307 -4.48545e-017 +vt 0.268182 1.28988 +vt 0.0698345 0.371018 +f 27/81/16 29/82/16 24/83/16 + +vt 0.519121 0.610402 +vn -3.8511e-017 0.226817 0.973937 +vt 0.174267 1.48488 +vt 0.0698345 0.610402 +f 24/84/17 29/85/17 23/86/17 + +f 24/58/3 25/59/3 27/72/3 + +f 26/73/3 27/72/3 25/59/3 + diff --git a/resources/Tree_01.obj.meta b/resources/Tree_01.obj.meta new file mode 100644 index 0000000..d90e553 --- /dev/null +++ b/resources/Tree_01.obj.meta @@ -0,0 +1,102 @@ +fileFormatVersion: 2 +guid: 30e3f3599a28f38b790ba48419797464 +ModelImporter: + serializedVersion: 20200 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + motionNodeName: + rigImportErrors: + rigImportWarnings: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/resources/Tree_02.mtl b/resources/Tree_02.mtl new file mode 100755 index 0000000..954f31b --- /dev/null +++ b/resources/Tree_02.mtl @@ -0,0 +1,14 @@ +# +## Alias OBJ Material File +# Exported from SketchUp, (c) 2000-2012 Trimble Navigation Limited + +newmtl Leafs +Ka 0.000000 0.000000 0.000000 +Kd 0.270588 0.407843 0.400000 +Ks 0.330000 0.330000 0.330000 + +newmtl Wood +Ka 0.000000 0.000000 0.000000 +Kd 0.666667 0.545098 0.356863 +Ks 0.330000 0.330000 0.330000 + diff --git a/resources/Tree_02.mtl.meta b/resources/Tree_02.mtl.meta new file mode 100644 index 0000000..cbb3758 --- /dev/null +++ b/resources/Tree_02.mtl.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: c7674ea23439a8e6d8c5d537866bcd90 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/resources/Tree_02.obj b/resources/Tree_02.obj new file mode 100755 index 0000000..bc5dbed --- /dev/null +++ b/resources/Tree_02.obj @@ -0,0 +1,229 @@ +# Alias OBJ Model File +# Exported from SketchUp, (c) 2000-2012 Trimble Navigation Limited +# File units = meters + +mtllib Tree_02.mtl + +g Mesh1 Tree Model + +usemtl Leafs +v 0 0 -1.18 +vt 4.4561e-018 -0.151761 +vn 7.96848e-018 0.282945 -0.959136 +v 0.177 0.6 -1.003 +vt -0.0804545 0.132585 +v 1.003 0.6 -1.003 +vt -0.455909 0.132585 +v 1.18 0 -1.18 +vt -0.536364 -0.151761 +f 1/1/1 2/2/1 3/3/1 4/4/1 + +v 0.177 0.6 -0.177 +vt -0.0804545 0.284347 +vn -0.959136 0.282945 6.61214e-018 +vt -0.455909 0.284347 +vt -0.536364 1.04622e-018 +v 0 0 -0 +vt 0 0 +f 5/5/2 2/6/2 1/7/2 6/8/2 + +vt -0.0804545 0.0804545 +vn 0 -1 -0 +v 0.0826 0.6 -0.0826 +vt -0.0375455 0.0375455 +vt -0.0804545 0.455909 +f 5/9/3 7/10/3 2/11/3 + +v 1.0974 0.6 -0.0826 +vt -0.498818 0.0375455 +f 7/10/3 5/9/3 8/12/3 + +v 1.003 0.6 -0.177 +vt -0.455909 0.0804545 +f 8/12/3 5/9/3 9/13/3 + +v 1.18 0 -0 +vt 0.536364 -1.26083e-018 +vn 7.96848e-018 0.282945 0.959136 +vt 0.455909 0.284347 +vt 0.0804545 0.284347 +f 10/14/4 9/15/4 5/16/4 6/8/4 + +vt 9.007e-019 -0.151761 +vn 0.959136 0.282945 1.61065e-018 +vt 0.536364 -0.151761 +vt 0.455909 0.132585 +vt 0.0804545 0.132585 +f 10/17/5 4/18/5 3/19/5 9/20/5 + +vt -0.536364 0 +v 0.786697 0 -0.393303 +vt -0.35759 0.178774 +vt -0.536364 0.536364 +f 10/21/3 11/22/3 4/23/3 + +v 0.393303 0 -0.393303 +vt -0.178774 0.178774 +f 10/21/3 12/24/3 11/22/3 + +f 6/8/3 12/24/3 10/21/3 + +v 0.393303 0 -0.786697 +vt -0.178774 0.35759 +f 12/24/3 6/8/3 13/25/3 + +vt 0 0.536364 +f 1/26/3 13/25/3 6/8/3 + +f 13/25/3 1/26/3 4/23/3 + +v 0.786697 0 -0.786697 +vt -0.35759 0.35759 +f 13/25/3 4/23/3 14/27/3 + +f 14/27/3 4/23/3 11/22/3 + +usemtl Wood +v 0.837838 -0.495868 -0.342162 +vt 0.155528 -0.263275 +vn 0.994724 0.102591 6.0893e-019 +v 0.837838 -0.495868 -0.837838 +vt 0.380836 -0.263275 +vt 0.35759 -0.0366853 +vt 0.178774 -0.0366853 +f 15/28/6 16/29/6 14/30/6 11/31/6 + +v 0.342162 -0.495868 -0.342162 +vt -0.155528 0.155528 +v 0.342162 -0.495868 -0.837838 +vt -0.155528 0.380836 +vt -0.380836 0.380836 +vt -0.380836 0.155528 +f 17/32/3 18/33/3 16/34/3 15/35/3 + +vt -0.178774 0.0183405 +vn -0.994724 0.102591 -5.80807e-018 +vt -0.35759 0.0183405 +vt -0.380836 -0.208249 +vt -0.155528 -0.208249 +f 12/36/7 13/37/7 18/38/7 17/39/7 + +usemtl Leafs +f 12/24/3 13/25/3 14/27/3 11/22/3 + +usemtl Wood +vt -0.155528 -0.263275 +vn 9.37084e-019 0.102591 -0.994724 +vt -0.178774 -0.0366853 +vt -0.35759 -0.0366853 +vt -0.380836 -0.263275 +f 18/40/8 13/41/8 14/42/8 16/43/8 + +vt 0.380836 -0.208249 +vn 7.35408e-018 0.102591 0.994724 +vt 0.35759 0.0183405 +vt 0.178774 0.0183405 +vt 0.155528 -0.208249 +f 15/44/9 11/45/9 12/46/9 17/47/9 + +usemtl Leafs +vt -0.455909 0.455909 +v 1.0974 0.6 -1.0974 +vt -0.498818 0.498818 +f 3/48/3 19/49/3 9/13/3 + +f 2/11/3 19/49/3 3/48/3 + +v 0.0826 0.6 -1.0974 +vt -0.0375455 0.498818 +f 2/11/3 20/50/3 19/49/3 + +f 20/50/3 2/11/3 7/10/3 + +v 0.23482 1.34304 -0.23482 +vt -0.106736 0.619473 +vn -0.979654 0.200693 -1.44372e-018 +v 0.23482 1.34304 -0.94518 +vt -0.429627 0.619473 +vt -0.498818 0.274714 +vt -0.0375455 0.274714 +f 21/51/10 22/52/10 20/53/10 7/54/10 + +vt -0.106736 0.106736 +v 0.153636 1.34304 -0.153636 +vt -0.0698345 0.0698345 +vt -0.106736 0.429627 +f 21/55/3 23/56/3 22/57/3 + +v 1.02636 1.34304 -0.153636 +vt -0.466529 0.0698345 +f 23/56/3 21/55/3 24/58/3 + +v 0.94518 1.34304 -0.23482 +vt -0.429627 0.106736 +f 24/58/3 21/55/3 25/59/3 + +vt 0.498818 0.274714 +vn -3.998e-018 0.200693 0.979654 +vt 0.429627 0.619473 +vt 0.106736 0.619473 +vt 0.0375455 0.274714 +f 8/60/11 25/61/11 21/62/11 7/63/11 + +vt 0.0375455 0.167069 +vn 0.979654 0.200693 -1.44372e-018 +vt 0.498818 0.167069 +v 0.94518 1.34304 -0.94518 +vt 0.429627 0.511829 +vt 0.106736 0.511829 +f 8/64/12 19/65/12 26/66/12 25/67/12 + +f 8/12/3 9/13/3 19/49/3 + +vt -0.0375455 0.167069 +vn -3.998e-018 0.200693 -0.979654 +vt -0.106736 0.511829 +vt -0.429627 0.511829 +vt -0.498818 0.167069 +f 20/68/13 22/69/13 26/70/13 19/71/13 + +v 1.02636 1.34304 -1.02636 +vt -0.466529 0.466529 +vt -0.429627 0.429627 +f 22/57/3 27/72/3 26/73/3 + +v 0.153636 1.34304 -1.02636 +vt -0.0698345 0.466529 +f 22/57/3 28/74/3 27/72/3 + +f 28/74/3 22/57/3 23/56/3 + +v 0.59 2.66304 -0.59 +vt -0.268182 1.23348 +vn -0.949465 0.313873 -0 +vt -0.466529 0.601542 +vt -0.0698345 0.601542 +f 29/75/14 28/76/14 23/77/14 + +vt -0.268182 1.06513 +vn -2.05913e-017 0.313873 -0.949465 +vt -0.466529 0.433192 +vt -0.0698345 0.433192 +f 29/78/15 27/79/15 28/80/15 + +vt 0.466529 0.433192 +vn 0.949465 0.313873 -0 +vt 0.268182 1.06513 +vt 0.0698345 0.433192 +f 27/81/16 29/82/16 24/83/16 + +vt 0.466529 0.601542 +vn -2.05913e-017 0.313873 0.949465 +vt 0.268182 1.23348 +vt 0.0698345 0.601542 +f 24/84/17 29/85/17 23/86/17 + +f 24/58/3 25/59/3 27/72/3 + +f 26/73/3 27/72/3 25/59/3 + diff --git a/resources/Tree_02.obj.meta b/resources/Tree_02.obj.meta new file mode 100644 index 0000000..7c31235 --- /dev/null +++ b/resources/Tree_02.obj.meta @@ -0,0 +1,102 @@ +fileFormatVersion: 2 +guid: 9b67280016de8aa3d876883a26aeb0f4 +ModelImporter: + serializedVersion: 20200 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + motionNodeName: + rigImportErrors: + rigImportWarnings: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/resources/bake.frag.spv b/resources/bake.frag.spv index dbd293829162151b6ac404866890ba941bf278c9..7079ce33fcf8b02cefcd0aaa5178a8781a0d52ff 100644 GIT binary patch delta 94 zcmdnO_=l03nMs+Qfq{{MgMoJN4|^b4pW?6l_eFWn`4w+{)+BG delta 202 zcmdnOy?~3CnMs+Qfq{{Moq>Zvbt11fzdQp212YgZ0qH}G3>(+WGBV0lbHQVvKn0x?M38p;Qm1QG+uD^IRuX^#NODgZG^CLbsYw3$H> uh!uc%A_FU!uLNYv05M415h$h%q*Z{J7l`?R*beA65Fe!OFVK7oAO--t!4O*j diff --git a/resources/cube.obj b/resources/cube.obj new file mode 100644 index 0000000..3ac1444 --- /dev/null +++ b/resources/cube.obj @@ -0,0 +1,43 @@ +o cube + +v -0.500000 -0.500000 0.500000 +v 0.500000 -0.500000 0.500000 +v -0.500000 0.500000 0.500000 +v 0.500000 0.500000 0.500000 +v -0.500000 0.500000 -0.500000 +v 0.500000 0.500000 -0.500000 +v -0.500000 -0.500000 -0.500000 +v 0.500000 -0.500000 -0.500000 + +vt 0.000000 0.000000 +vt 1.000000 0.000000 +vt 0.000000 1.000000 +vt 1.000000 1.000000 + +vn 0.000000 0.000000 1.000000 +vn 0.000000 1.000000 0.000000 +vn 0.000000 0.000000 -1.000000 +vn 0.000000 -1.000000 0.000000 +vn 1.000000 0.000000 0.000000 +vn -1.000000 0.000000 0.000000 + +g cube +usemtl cube +s 1 +f 1/1/1 2/2/1 3/3/1 +f 3/3/1 2/2/1 4/4/1 +s 2 +f 3/1/2 4/2/2 5/3/2 +f 5/3/2 4/2/2 6/4/2 +s 3 +f 5/4/3 6/3/3 7/2/3 +f 7/2/3 6/3/3 8/1/3 +s 4 +f 7/1/4 8/2/4 1/3/4 +f 1/3/4 8/2/4 2/4/4 +s 5 +f 2/1/5 8/2/5 4/3/5 +f 4/3/5 8/2/5 6/4/5 +s 6 +f 7/1/6 1/2/6 5/3/6 +f 5/3/6 1/2/6 3/4/6 \ No newline at end of file diff --git a/resources/forward.frag.spv b/resources/forward.frag.spv index bc571bb9bcf4478d82df32a94a8ad2ad9b28cdd0..b3fad0c802994f9984b3e5d0b9edceb8b67f6d08 100644 GIT binary patch delta 95 zcmZosIiteO%%sfDz`)4B!N5C_JJ+3sfs4W2C*IxP-`&SGKDnSEzPO|^CpA7NGcPqh gC9^0sxg@hJm4Sf;r~#oaGcP%(GzCe)#`0c40ERIfF8}}l delta 23 ecmX@3(xSr6%%sfDz`)4B&cHE|J9p!O9zg&!0tEU1 diff --git a/resources/forward.vert b/resources/forward.vert index 5e02939..4cada40 100644 --- a/resources/forward.vert +++ b/resources/forward.vert @@ -1,7 +1,7 @@ #version 450 -layout(location = 0) in ivec4 a_Pos; -layout(location = 1) in ivec4 a_Normal; +layout(location = 0) in vec4 a_Pos; +layout(location = 1) in vec4 a_Normal; layout(location = 0) out vec3 v_Normal; layout(location = 1) out vec4 v_Position; diff --git a/resources/forward.vert.spv b/resources/forward.vert.spv index 9bd02dfae3d0d07909d3d1c8ba557d06fb346247..fea2df5dadf707648a904adc818af3dac06bd4b0 100644 GIT binary patch literal 2148 zcmZ9NYflqV5QYzw1-u|4a#0Zrct`ODh$4tVQa&_LLxLYSwcX&FrAyi^ivPmD zVJq;PVLNCf;ce#ye=eQj;Pci!G`kQeSx8+q*pV=WV{nXFo_0s;1{aV}!lQ352+(o~2FmX?r7sU@E zzZSIjgLV@9kSa98k+E5Vem9W%Q%8!2tz<9!sYwu%*_fehn^Ac5G3+GuRwFpIu@!9H zC_1lMAp-->u8ioevOPFe>#r|a*B!S`A_{+87Tp3P&Ydsqz*z9t>Tm)zUbTRD`A zJx}NQc0OlRPqeLH=FXYR*cnWZ&RrFmA9vxmA*$#eV(FE8*u$&{AJbymoa^H1vYO{K$Boj*wEg z@3J|*mQHS)8#|>(aN^&H(>FNr=}~EQfpk2;psg&@VH)CSuOaa!sD(9uvFFjB*-z)pIJa=-;zGKTnz3izh!iQtnjASr;4V%p|YNQ5cF()dE@W=j3Il=FW zs1N(PrqA~QX0v}F`TXo33In%39!uuk+5Pj9!8nFJkqlO>^J$Jzr}-`981JB_3YpV0 z5jn|03_jG4jsL8v2OsL^djPZgS0(e-if`wgba1PGO)_zob6qk%;N;;udoSY6lZSiI L@%yWeRz&{*RB@1F literal 1948 zcmZ9MX-^YT6oxO|Kv5Bq#jQ@MJ6bnDlxQSq%7+GPNbuvNN`qsj9W$Nc@-O^L{wlwi z_&zgpO64|Z&N=Tr+kNkaVxd0i+=QESQ*P5`Yt|KnI5+JoscyFRT8%++&{$o2M8r8) zOaqCTai>$eq;Ibi^l?r}s*;x44Xm^0FE z`$>@eaGIp-@L5CuLllPxBaAtQ{&o~bv7(BaFI&`Qj?RB|%jsN4emfcjNf2pb=3&kb zm~>f{A4(sFe!Cm*cjKh{U9Qj!$7at8RKyug{nn17FWG$H86UgDUMo2KoD7s- zbZcp}YN(do573T$s8NlGyFl;d19k z^Mc`5_2qsHquyEh^aZCLKKBMTFMmeblT1ifa-aKWkv}U1f$LF1}Z~43vYBKMAX-~ppUi=4=tY^tJWWx_wRTgi+ zIAafCPX5u+gMIqoXaC1Co}b94w(-iH!vQ$?tJ3fSPCh<&54Ntij3$>wpFcu)dt3U# z=>-Y*0M{=SN$oia_yf@vm_fFVV%smB#a#D zSk7hH;0Vid+TxTqa7Dr_)FzfQaOa+c`=${)@peo0N#1S?1GgFO%I3|`10L^5!03U$A|VHy9=ta&>#;7IyBxopXY#@6L5&Rw cc~ = cgmath::Matrix4::new( - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, 0.0, - 0.0, 0.0, 0.5, 0.0, - 0.0, 0.0, 0.5, 1.0, -); - -#[allow(dead_code)] -pub fn cast_slice(data: &[T]) -> &[u8] { - use std::{mem::size_of, slice::from_raw_parts}; - - unsafe { from_raw_parts(data.as_ptr() as *const u8, data.len() * size_of::()) } -} -#[allow(dead_code)] -pub enum ShaderStage { - Vertex, - Fragment, - Compute, -} -pub trait Example: 'static + Sized { +/* +pub trait Runtime: 'static + Sized { fn optional_features() -> wgpu::Features { wgpu::Features::empty() } @@ -60,253 +31,21 @@ pub trait Example: 'static + Sized { spawner: &impl LocalSpawn, ); } +*/ -struct Setup { - window: winit::window::Window, - event_loop: EventLoop<()>, - instance: wgpu::Instance, - size: winit::dpi::PhysicalSize, - surface: wgpu::Surface, - adapter: wgpu::Adapter, - device: wgpu::Device, - queue: wgpu::Queue, -} - -async fn setup(title: &str) -> Setup { - #[cfg(not(target_arch = "wasm32"))] - { - let chrome_tracing_dir = std::env::var("WGPU_CHROME_TRACE"); - wgpu_subscriber::initialize_default_subscriber( - chrome_tracing_dir.as_ref().map(std::path::Path::new).ok(), - ); - }; - - #[cfg(target_arch = "wasm32")] - console_log::init().expect("could not initialize logger"); - - let event_loop = EventLoop::new(); - let mut builder = winit::window::WindowBuilder::new(); - builder = builder.with_title(title); - #[cfg(windows_OFF)] // TODO - { - use winit::platform::windows::WindowBuilderExtWindows; - builder = builder.with_no_redirection_bitmap(true); - } - let window = builder.build(&event_loop).unwrap(); - - log::info!("Initializing the surface..."); - - let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); - let (size, surface) = unsafe { - let size = window.inner_size(); - let surface = instance.create_surface(&window); - (size, surface) - }; - - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::HighPerformance, - compatible_surface: Some(&surface), - }) - .await - .unwrap(); - - let optional_features = E::optional_features(); - let required_features = E::required_features(); - let adapter_features = adapter.features(); - assert!( - adapter_features.contains(required_features), - "Adapter does not support required features for this example: {:?}", - required_features - adapter_features - ); - - let needed_limits = E::required_limits(); - - let trace_dir = std::env::var("WGPU_TRACE"); - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - features: (optional_features & adapter_features) | required_features, - limits: needed_limits, - shader_validation: true, - }, - trace_dir.ok().as_ref().map(std::path::Path::new), - ) - .await - .unwrap(); - - Setup { - window, - event_loop, - instance, - size, - surface, - adapter, - device, - queue, - } -} - -fn start( - Setup { - window, - event_loop, - instance, - size, - surface, - adapter, - device, - queue, - }: Setup, -) { - #[cfg(not(target_arch = "wasm32"))] - let (mut pool, spawner) = { - let local_pool = futures::executor::LocalPool::new(); - let spawner = local_pool.spawner(); - (local_pool, spawner) - }; - - #[cfg(target_arch = "wasm32")] - let spawner = { - use futures::{future::LocalFutureObj, task::SpawnError}; - use winit::platform::web::WindowExtWebSys; - - struct WebSpawner {} - impl LocalSpawn for WebSpawner { - fn spawn_local_obj( - &self, - future: LocalFutureObj<'static, ()>, - ) -> Result<(), SpawnError> { - Ok(wasm_bindgen_futures::spawn_local(future)) - } - } - - std::panic::set_hook(Box::new(console_error_panic_hook::hook)); - - // On wasm, append the canvas to the document body - web_sys::window() - .and_then(|win| win.document()) - .and_then(|doc| doc.body()) - .and_then(|body| { - body.append_child(&web_sys::Element::from(window.canvas())) - .ok() - }) - .expect("couldn't append canvas to document body"); - - WebSpawner {} - }; - - let mut sc_desc = wgpu::SwapChainDescriptor { - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - // TODO: Allow srgb unconditionally - format: if cfg!(target_arch = "wasm32") { - wgpu::TextureFormat::Bgra8Unorm - } else { - wgpu::TextureFormat::Bgra8UnormSrgb - }, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Mailbox, - }; - let mut swap_chain = device.create_swap_chain(&surface, &sc_desc); - - log::info!("Initializing the example..."); - let mut example = E::init(&sc_desc, &device, &queue); - - #[cfg(not(target_arch = "wasm32"))] - let mut last_update_inst = Instant::now(); - - log::info!("Entering render loop..."); - event_loop.run(move |event, _, control_flow| { - let _ = (&instance, &adapter); // force ownership by the closure - *control_flow = if cfg!(feature = "metal-auto-capture") { - ControlFlow::Exit - } else { - #[cfg(not(target_arch = "wasm32"))] - { - ControlFlow::WaitUntil(Instant::now() + Duration::from_millis(10)) - } - #[cfg(target_arch = "wasm32")] - { - ControlFlow::Poll - } - }; - match event { - event::Event::MainEventsCleared => { - #[cfg(not(target_arch = "wasm32"))] - { - if last_update_inst.elapsed() > Duration::from_millis(20) { - window.request_redraw(); - last_update_inst = Instant::now(); - } - - pool.run_until_stalled(); - } - - #[cfg(target_arch = "wasm32")] - window.request_redraw(); - } - event::Event::WindowEvent { - event: WindowEvent::Resized(size), - .. - } => { - log::info!("Resizing to {:?}", size); - sc_desc.width = size.width; - sc_desc.height = size.height; - example.resize(&sc_desc, &device, &queue); - swap_chain = device.create_swap_chain(&surface, &sc_desc); - } - event::Event::WindowEvent { event, .. } => match event { - WindowEvent::KeyboardInput { - input: - event::KeyboardInput { - virtual_keycode: Some(event::VirtualKeyCode::Escape), - state: event::ElementState::Pressed, - .. - }, - .. - } - | WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - } - _ => { - example.update(event); - } - }, - event::Event::RedrawRequested(_) => { - let frame = match swap_chain.get_current_frame() { - Ok(frame) => frame, - Err(_) => { - swap_chain = device.create_swap_chain(&surface, &sc_desc); - swap_chain - .get_current_frame() - .expect("Failed to acquire next swap chain texture!") - } - }; - - example.render(&frame.output, &device, &queue, &spawner); - } - _ => {} - } - }); -} - +/* #[cfg(not(target_arch = "wasm32"))] -pub fn run(title: &str) { +pub fn run(title: &str) { let setup = futures::executor::block_on(setup::(title)); start::(setup); } #[cfg(target_arch = "wasm32")] -pub fn run(title: &str) { +pub fn run(title: &str) { let title = title.to_owned(); wasm_bindgen_futures::spawn_local(async move { let setup = setup::(&title).await; start::(setup); }); } - -// This allows treating the framework as a standalone example, -// thus avoiding listing the example names in `Cargo.toml`. -#[allow(dead_code)] -fn main() {} +*/ \ No newline at end of file diff --git a/src/geometry.rs b/src/geometry.rs new file mode 100644 index 0000000..7039c02 --- /dev/null +++ b/src/geometry.rs @@ -0,0 +1,87 @@ +use bytemuck::{Pod, Zeroable}; + +#[repr(C)] +#[derive(Clone, Copy, Debug)] +struct Vertex { + _pos: [f32; 4], + _normal: [f32; 4], +} + +unsafe impl Pod for Vertex {} + +unsafe impl Zeroable for Vertex {} + +fn vertex(pos: [f32; 3], nor: [f32; 3]) -> Vertex { + Vertex { + _pos: [pos[0], pos[1], pos[2], 1.0], + _normal: [nor[0], nor[1], nor[2], 0.0], + } +} + +fn import_mesh(mesh_path: &str) -> (Vec, Vec) { + //let obj_file = "/home/mrh/source/3d-min-viable-eng/resources/Tree_01.obj"; + //let mtl_file = "/home/mrh/source/3d-min-viable-eng/resources/Tree_01.mtl"; + let (models, materials) = tobj::load_obj(mesh_path, false).expect("Failed to load file"); + + //let q = tobj::load_mtl(mtl_file).unwrap(); + + println!("# of models: {}", models.len()); + println!("# of materials: {}", materials.len()); + + //let model = models.get(2).unwrap(); + //let mesh = &model.mesh; + + let mut index_data : Vec = Vec::new(); + let mut vertex_data = Vec::new(); + + for model in models { + let mesh = &model.mesh; + let mut next_face = 0; + for f in 0..mesh.num_face_indices.len() { + let end = next_face + mesh.num_face_indices[f] as usize; + let face_indices: Vec<_> = mesh.indices[next_face..end].iter().collect(); + + for i in face_indices { + index_data.push(*i); + } + + //println!(" face[{}] = {:?}", f, face_indices); + next_face = end; + } + + // Normals and texture coordinates are also loaded, but not printed in this example + //println!("model[{}].vertices: {}", i, mesh.positions.len() / 3); + assert!(mesh.positions.len() % 3 == 0); + + for v in 0..mesh.positions.len() / 3 { + vertex_data.push( + vertex([ + mesh.positions[3 * v], + mesh.positions[3 * v + 1], + mesh.positions[3 * v + 2] + ], + [ + mesh.normals[3 * v], + mesh.normals[3 * v + 1], + mesh.normals[3 * v + 2] + ], + )); + } + } + println!("{:?}\n\n\n\n\n {:?}", vertex_data, index_data); + (vertex_data.to_vec(), index_data.to_vec()) + +} + +fn create_plane(size: f32) -> (Vec, Vec) { + let vertex_data = [ + vertex([size, -size, 0.0], [0.0, 0.0, 1.0]), + vertex([size, size, 0.0], [0.0, 0.0, 1.0]), + vertex([-size, -size, 0.0], [0.0, 0.0, 1.0]), + vertex([-size, size, 0.0], [0.0, 0.0, 1.0]), + ]; + + let index_data: &[u32] = &[0, 1, 2, 2, 1, 3]; + + (vertex_data.to_vec(), index_data.to_vec()) +} \ No newline at end of file diff --git a/src/light.rs b/src/light.rs new file mode 100644 index 0000000..604b69f --- /dev/null +++ b/src/light.rs @@ -0,0 +1,49 @@ +use bytemuck::__core::ops::Range; +use bytemuck::{Zeroable, Pod}; + +struct Light { + pos: cgmath::Point3, + color: wgpu::Color, + fov: f32, + depth: Range, + target_view: wgpu::TextureView, +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct LightRaw { + proj: [[f32; 4]; 4], + pos: [f32; 4], + color: [f32; 4], +} + +unsafe impl Pod for LightRaw {} + +unsafe impl Zeroable for LightRaw {} + +impl Light { + fn to_raw(&self) -> LightRaw { + use cgmath::{Deg, EuclideanSpace, Matrix4, PerspectiveFov, Point3, Vector3}; + + let mx_view = Matrix4::look_at(self.pos, Point3::origin(), Vector3::unit_z()); + let projection = PerspectiveFov { + fovy: Deg(self.fov).into(), + aspect: 1.0, + near: self.depth.start, + far: self.depth.end, + }; + let mx_correction = framework::OPENGL_TO_WGPU_MATRIX; + let mx_view_proj = + mx_correction * cgmath::Matrix4::from(projection.to_perspective()) * mx_view; + LightRaw { + proj: *mx_view_proj.as_ref(), + pos: [self.pos.x, self.pos.y, self.pos.z, 1.0], + color: [ + self.color.r as f32, + self.color.g as f32, + self.color.b as f32, + 1.0, + ], + } + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c74da62..0bdeff6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,854 +1,316 @@ +extern crate tobj; +extern crate winit; + use std::{iter, mem, num::NonZeroU32, ops::Range, rc::Rc}; +#[cfg(not(target_arch = "wasm32"))] +use std::time::{Duration, Instant}; -extern crate winit; +use bytemuck::{Pod, Zeroable}; +use futures::task::LocalSpawn; +use wgpu::util::DeviceExt; +use wgpu_subscriber; +use winit::{ + event::{self, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, +}; -#[path = "framework.rs"] mod framework; +mod geometry; +mod light; +mod render; +mod runtime; +/* +Collision detection +https://crates.io/crates/mgf -use bytemuck::{Pod, Zeroable}; -use wgpu::util::DeviceExt; +Obj file format +http://paulbourke.net/dataformats/obj/ -#[repr(C)] -#[derive(Clone, Copy)] +tobj obj loader +https://docs.rs/tobj/2.0.3/tobj/index.html -struct Vertex { - _pos: [i8; 4], - _normal: [i8; 4], -} +mesh generator lib, might be useful +https://docs.rs/genmesh/0.6.2/genmesh/ -unsafe impl Pod for Vertex {} -unsafe impl Zeroable for Vertex {} +legion ECS +https://github.com/amethyst/legion -fn vertex(pos: [i8; 3], nor: [i8; 3]) -> Vertex { - Vertex { - _pos: [pos[0], pos[1], pos[2], 1], - _normal: [nor[0], nor[1], nor[2], 0], - } -} -fn create_cube() -> (Vec, Vec) { - let vertex_data = [ - // top (0, 0, 1) - vertex([-1, -1, 1], [0, 0, 1]), - vertex([1, -1, 1], [0, 0, 1]), - vertex([1, 1, 1], [0, 0, 1]), - vertex([-1, 1, 1], [0, 0, 1]), - // bottom (0, 0, -1) - vertex([-1, 1, -1], [0, 0, -1]), - vertex([1, 1, -1], [0, 0, -1]), - vertex([1, -1, -1], [0, 0, -1]), - vertex([-1, -1, -1], [0, 0, -1]), - // right (1, 0, 0) - vertex([1, -1, -1], [1, 0, 0]), - vertex([1, 1, -1], [1, 0, 0]), - vertex([1, 1, 1], [1, 0, 0]), - vertex([1, -1, 1], [1, 0, 0]), - // left (-1, 0, 0) - vertex([-1, -1, 1], [-1, 0, 0]), - vertex([-1, 1, 1], [-1, 0, 0]), - vertex([-1, 1, -1], [-1, 0, 0]), - vertex([-1, -1, -1], [-1, 0, 0]), - // front (0, 1, 0) - vertex([1, 1, -1], [0, 1, 0]), - vertex([-1, 1, -1], [0, 1, 0]), - vertex([-1, 1, 1], [0, 1, 0]), - vertex([1, 1, 1], [0, 1, 0]), - // back (0, -1, 0) - vertex([1, -1, 1], [0, -1, 0]), - vertex([-1, -1, 1], [0, -1, 0]), - vertex([-1, -1, -1], [0, -1, 0]), - vertex([1, -1, -1], [0, -1, 0]), - ]; - - let index_data: &[u16] = &[ - 0, 1, 2, 2, 3, 0, // top - 4, 5, 6, 6, 7, 4, // bottom - 8, 9, 10, 10, 11, 8, // right - 12, 13, 14, 14, 15, 12, // left - 16, 17, 18, 18, 19, 16, // front - 20, 21, 22, 22, 23, 20, // back - ]; - - (vertex_data.to_vec(), index_data.to_vec()) -} +mvp: -fn create_plane(size: i8) -> (Vec, Vec) { - let vertex_data = [ - vertex([size, -size, 0], [0, 0, 1]), - vertex([size, size, 0], [0, 0, 1]), - vertex([-size, -size, 0], [0, 0, 1]), - vertex([-size, size, 0], [0, 0, 1]), - ]; +ECS + animation + render 3d + input/io + collision / physics + entities & behaviours - let index_data: &[u16] = &[0, 1, 2, 2, 1, 3]; + */ - (vertex_data.to_vec(), index_data.to_vec()) -} -struct Entity { - mx_world: cgmath::Matrix4, - rotation_speed: f32, - color: wgpu::Color, - vertex_buf: Rc, - index_buf: Rc, - index_count: usize, - bind_group: wgpu::BindGroup, - uniform_buf: wgpu::Buffer, -} -struct Light { - pos: cgmath::Point3, - color: wgpu::Color, - fov: f32, - depth: Range, - target_view: wgpu::TextureView, -} - -#[repr(C)] -#[derive(Clone, Copy)] -struct LightRaw { - proj: [[f32; 4]; 4], - pos: [f32; 4], - color: [f32; 4], -} -unsafe impl Pod for LightRaw {} -unsafe impl Zeroable for LightRaw {} +#[cfg_attr(rustfmt, rustfmt_skip)] +#[allow(unused)] +pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4 = cgmath::Matrix4::new( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, + 0.0, 0.0, 0.5, 1.0, +); -impl Light { - fn to_raw(&self) -> LightRaw { - use cgmath::{Deg, EuclideanSpace, Matrix4, PerspectiveFov, Point3, Vector3}; +#[allow(dead_code)] +pub fn cast_slice(data: &[T]) -> &[u8] { + use std::{mem::size_of, slice::from_raw_parts}; - let mx_view = Matrix4::look_at(self.pos, Point3::origin(), Vector3::unit_z()); - let projection = PerspectiveFov { - fovy: Deg(self.fov).into(), - aspect: 1.0, - near: self.depth.start, - far: self.depth.end, - }; - let mx_correction = framework::OPENGL_TO_WGPU_MATRIX; - let mx_view_proj = - mx_correction * cgmath::Matrix4::from(projection.to_perspective()) * mx_view; - LightRaw { - proj: *mx_view_proj.as_ref(), - pos: [self.pos.x, self.pos.y, self.pos.z, 1.0], - color: [ - self.color.r as f32, - self.color.g as f32, - self.color.b as f32, - 1.0, - ], - } - } + unsafe { from_raw_parts(data.as_ptr() as *const u8, data.len() * size_of::()) } } -#[repr(C)] -#[derive(Clone, Copy)] -struct ForwardUniforms { - proj: [[f32; 4]; 4], - num_lights: [u32; 4], +#[allow(dead_code)] +pub enum ShaderStage { + Vertex, + Fragment, + Compute, } -unsafe impl Pod for ForwardUniforms {} -unsafe impl Zeroable for ForwardUniforms {} +/* + window: winit::window::Window, + event_loop: EventLoop<()>, + instance: wgpu::Instance, + size: winit::dpi::PhysicalSize, + surface: wgpu::Surface, + adapter: wgpu::Adapter, + device: wgpu::Device, + queue: wgpu::Queue, + */ -#[repr(C)] -#[derive(Clone, Copy)] -struct EntityUniforms { - model: [[f32; 4]; 4], - color: [f32; 4], -} +fn main() { -unsafe impl Pod for EntityUniforms {} -unsafe impl Zeroable for EntityUniforms {} + // #[cfg(not(target_arch = "wasm32"))] + // { + // let chrome_tracing_dir = std::env::var("WGPU_CHROME_TRACE"); + // wgpu_subscriber::initialize_default_subscriber( + // chrome_tracing_dir.as_ref().map(std::path::Path::new).ok(), + // ); + // }; + // #[cfg(target_arch = "wasm32")] + // console_log::init().expect("could not initialize logger"); -#[repr(C)] -struct ShadowUniforms { - proj: [[f32; 4]; 4], -} -struct Pass { - pipeline: wgpu::RenderPipeline, - bind_group: wgpu::BindGroup, - uniform_buf: wgpu::Buffer, -} + let event_loop = EventLoop::new(); + let mut builder = winit::window::WindowBuilder::new(); + builder = builder.with_title(title); -struct Example { - entities: Vec, - lights: Vec, - lights_are_dirty: bool, - shadow_pass: Pass, - forward_pass: Pass, - forward_depth: wgpu::TextureView, - light_uniform_buf: wgpu::Buffer, -} + // I don't know what they are doing here + #[cfg(windows_OFF)] // TODO + { + use winit::platform::windows::WindowBuilderExtWindows; + builder = builder.with_no_redirection_bitmap(true); + } -impl Example { - const MAX_LIGHTS: usize = 10; - const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; - const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d { - width: 512, - height: 512, - depth: Self::MAX_LIGHTS as u32, + let window = builder.build(&event_loop).unwrap(); + + log::info!("Initializing the surface..."); + + // Grab the GPU instance, and query its features + let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); + let (size, surface) = unsafe { + let size = window.inner_size(); + let surface = instance.create_surface(&window); + (size, surface) }; - const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; - - fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { - let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 20.0); - let mx_view = cgmath::Matrix4::look_at( - cgmath::Point3::new(3.0f32, -10.0, 6.0), - cgmath::Point3::new(0f32, 0.0, 0.0), - cgmath::Vector3::unit_z(), - ); - let mx_correction = framework::OPENGL_TO_WGPU_MATRIX; - mx_correction * mx_projection * mx_view - } -} -impl framework::Example for Example { - fn optional_features() -> wgpu::Features { - wgpu::Features::DEPTH_CLAMPING - } - - fn init( - sc_desc: &wgpu::SwapChainDescriptor, - device: &wgpu::Device, - _queue: &wgpu::Queue, - ) -> Self { - // Create the vertex and index buffers - let vertex_size = mem::size_of::(); - let (cube_vertex_data, cube_index_data) = create_cube(); - let cube_vertex_buf = Rc::new(device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Cubes Vertex Buffer"), - contents: bytemuck::cast_slice(&cube_vertex_data), - usage: wgpu::BufferUsage::VERTEX, + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + }) + .await + .unwrap(); + + let optional_features = E::optional_features(); + let required_features = E::required_features(); + let adapter_features = adapter.features(); + assert!( + adapter_features.contains(required_features), + "Adapter does not support required features for this example: {:?}", + required_features - adapter_features + ); + + let needed_limits = E::required_limits(); + + // Maybe for debug tracing??? + let trace_dir = std::env::var("WGPU_TRACE"); + + // And then get the device we want + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + features: (optional_features & adapter_features) | required_features, + limits: needed_limits, + shader_validation: true, }, - )); + trace_dir.ok().as_ref().map(std::path::Path::new), + ) + .await + .unwrap(); - let cube_index_buf = Rc::new(device.create_buffer_init( - &wgpu::util::BufferInitDescriptor { - label: Some("Cubes Index Buffer"), - contents: bytemuck::cast_slice(&cube_index_data), - usage: wgpu::BufferUsage::INDEX, - }, - )); - - let (plane_vertex_data, plane_index_data) = create_plane(7); - let plane_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Plane Vertex Buffer"), - contents: bytemuck::cast_slice(&plane_vertex_data), - usage: wgpu::BufferUsage::VERTEX, - }); - - let plane_index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Plane Index Buffer"), - contents: bytemuck::cast_slice(&plane_index_data), - usage: wgpu::BufferUsage::INDEX, - }); - - let entity_uniform_size = mem::size_of::() as wgpu::BufferAddress; - let plane_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: entity_uniform_size, - usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - mapped_at_creation: false, - }); - - let local_bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::UniformBuffer { - dynamic: false, - min_binding_size: wgpu::BufferSize::new( - mem::size_of::() as _ - ), - }, - count: None, - }], - label: None, - }); - - let mut entities = vec![{ - use cgmath::SquareMatrix; - - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &local_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(plane_uniform_buf.slice(..)), - }], - label: None, - }); - Entity { - mx_world: cgmath::Matrix4::identity(), - rotation_speed: 0.0, - color: wgpu::Color::WHITE, - vertex_buf: Rc::new(plane_vertex_buf), - index_buf: Rc::new(plane_index_buf), - index_count: plane_index_data.len(), - bind_group, - uniform_buf: plane_uniform_buf, - } - }]; - struct CubeDesc { - offset: cgmath::Vector3, - angle: f32, - scale: f32, - rotation: f32, - } - let cube_descs = [ - CubeDesc { - offset: cgmath::vec3(-2.0, -2.0, 2.0), - angle: 10.0, - scale: 0.7, - rotation: 0.1, - }, - CubeDesc { - offset: cgmath::vec3(2.0, -2.0, 2.0), - angle: 50.0, - scale: 1.3, - rotation: 0.2, - }, - CubeDesc { - offset: cgmath::vec3(-2.0, 2.0, 2.0), - angle: 140.0, - scale: 1.1, - rotation: 0.3, - }, - CubeDesc { - offset: cgmath::vec3(2.0, 2.0, 2.0), - angle: 210.0, - scale: 0.9, - rotation: 0.4, - }, - ]; - - for cube in &cube_descs { - use cgmath::{Decomposed, Deg, InnerSpace, Quaternion, Rotation3}; - - let transform = Decomposed { - disp: cube.offset.clone(), - rot: Quaternion::from_axis_angle(cube.offset.normalize(), Deg(cube.angle)), - scale: cube.scale, - }; - let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: entity_uniform_size, - usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - mapped_at_creation: false, - }); - entities.push(Entity { - mx_world: cgmath::Matrix4::from(transform), - rotation_speed: cube.rotation, - color: wgpu::Color::GREEN, - vertex_buf: Rc::clone(&cube_vertex_buf), - index_buf: Rc::clone(&cube_index_buf), - index_count: cube_index_data.len(), - bind_group: device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &local_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), - }], - label: None, - }), - uniform_buf, - }); + #[cfg(not(target_arch = "wasm32"))] + let (mut pool, spawner) = { + let local_pool = futures::executor::LocalPool::new(); + let spawner = local_pool.spawner(); + (local_pool, spawner) + }; + + // This is some gross-ass web shit + /*#[cfg(target_arch = "wasm32")] + let spawner = { + use futures::{future::LocalFutureObj, task::SpawnError}; + use winit::platform::web::WindowExtWebSys; + + struct WebSpawner {} + impl LocalSpawn for WebSpawner { + fn spawn_local_obj( + &self, + future: LocalFutureObj<'static, ()>, + ) -> Result<(), SpawnError> { + Ok(wasm_bindgen_futures::spawn_local(future)) + } } - // Create other resources - let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor { - label: Some("shadow"), - address_mode_u: wgpu::AddressMode::ClampToEdge, - address_mode_v: wgpu::AddressMode::ClampToEdge, - address_mode_w: wgpu::AddressMode::ClampToEdge, - mag_filter: wgpu::FilterMode::Linear, - min_filter: wgpu::FilterMode::Linear, - mipmap_filter: wgpu::FilterMode::Nearest, - compare: Some(wgpu::CompareFunction::LessEqual), - ..Default::default() - }); - - let shadow_texture = device.create_texture(&wgpu::TextureDescriptor { - size: Self::SHADOW_SIZE, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: Self::SHADOW_FORMAT, - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, - label: None, - }); - let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default()); - - let mut shadow_target_views = (0..2) - .map(|i| { - Some(shadow_texture.create_view(&wgpu::TextureViewDescriptor { - label: Some("shadow"), - format: None, - dimension: Some(wgpu::TextureViewDimension::D2), - aspect: wgpu::TextureAspect::All, - base_mip_level: 0, - level_count: None, - base_array_layer: i as u32, - array_layer_count: NonZeroU32::new(1), - })) + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + + // On wasm, append the canvas to the document body + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.body()) + .and_then(|body| { + body.append_child(&web_sys::Element::from(window.canvas())) + .ok() }) - .collect::>(); - let lights = vec![ - Light { - pos: cgmath::Point3::new(7.0, -5.0, 10.0), - color: wgpu::Color { - r: 0.5, - g: 1.0, - b: 0.5, - a: 1.0, - }, - fov: 60.0, - depth: 1.0..20.0, - target_view: shadow_target_views[0].take().unwrap(), - }, - Light { - pos: cgmath::Point3::new(-5.0, 7.0, 10.0), - color: wgpu::Color { - r: 1.0, - g: 0.5, - b: 0.5, - a: 1.0, - }, - fov: 45.0, - depth: 1.0..20.0, - target_view: shadow_target_views[1].take().unwrap(), - }, - ]; - let light_uniform_size = - (Self::MAX_LIGHTS * mem::size_of::()) as wgpu::BufferAddress; - let light_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: light_uniform_size, - usage: wgpu::BufferUsage::UNIFORM - | wgpu::BufferUsage::COPY_SRC - | wgpu::BufferUsage::COPY_DST, - mapped_at_creation: false, - }); - - let vertex_attr = wgpu::vertex_attr_array![0 => Char4, 1 => Char4]; - let vb_desc = wgpu::VertexBufferDescriptor { - stride: vertex_size as wgpu::BufferAddress, - step_mode: wgpu::InputStepMode::Vertex, - attributes: &vertex_attr, - }; + .expect("couldn't append canvas to document body"); + + WebSpawner {} + };*/ + + // Swapchain has a prototype dealio + let mut sc_desc = wgpu::SwapChainDescriptor { + // Allows a texture to be a output attachment of a renderpass. + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + format: if cfg!(target_arch = "wasm32") { + wgpu::TextureFormat::Bgra8Unorm + } else { + wgpu::TextureFormat::Bgra8UnormSrgb + }, + width: size.width, + height: size.height, + // The presentation engine waits for the next vertical blanking period to update + present_mode: wgpu::PresentMode::Mailbox, + }; - let shadow_pass = { - let uniform_size = mem::size_of::() as wgpu::BufferAddress; - // Create pipeline layout - let bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - label: None, - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, // global - visibility: wgpu::ShaderStage::VERTEX, - ty: wgpu::BindingType::UniformBuffer { - dynamic: false, - min_binding_size: wgpu::BufferSize::new(uniform_size), - }, - count: None, - }], - }); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("shadow"), - bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout], - push_constant_ranges: &[], - }); - - let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: uniform_size, - usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - mapped_at_creation: false, - }); - - // Create bind group - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), - }], - label: None, - }); - - // Create the render pipeline - let vs_module = device.create_shader_module(wgpu::include_spirv!("../resources/bake.vert.spv")); - let fs_module = device.create_shader_module(wgpu::include_spirv!("../resources/bake.frag.spv")); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("shadow"), - layout: Some(&pipeline_layout), - vertex_stage: wgpu::ProgrammableStageDescriptor { - module: &vs_module, - entry_point: "main", - }, - fragment_stage: Some(wgpu::ProgrammableStageDescriptor { - module: &fs_module, - entry_point: "main", - }), - rasterization_state: Some(wgpu::RasterizationStateDescriptor { - front_face: wgpu::FrontFace::Ccw, - cull_mode: wgpu::CullMode::Back, - depth_bias: 2, // corresponds to bilinear filtering - depth_bias_slope_scale: 2.0, - depth_bias_clamp: 0.0, - clamp_depth: device.features().contains(wgpu::Features::DEPTH_CLAMPING), - }), - primitive_topology: wgpu::PrimitiveTopology::TriangleList, - color_states: &[], - depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { - format: Self::SHADOW_FORMAT, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::LessEqual, - stencil: wgpu::StencilStateDescriptor::default(), - }), - vertex_state: wgpu::VertexStateDescriptor { - index_format: wgpu::IndexFormat::Uint16, - vertex_buffers: &[vb_desc.clone()], - }, - sample_count: 1, - sample_mask: !0, - alpha_to_coverage_enabled: false, - }); - - Pass { - pipeline, - bind_group, - uniform_buf, - } - }; + let mut swap_chain = device.create_swap_chain(&surface, &sc_desc); + log::info!("Done doing the loading part..."); - let forward_pass = { - // Create pipeline layout - let bind_group_layout = - device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, // global - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::UniformBuffer { - dynamic: false, - min_binding_size: wgpu::BufferSize::new(mem::size_of::< - ForwardUniforms, - >( - ) - as _), - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, // lights - visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::UniformBuffer { - dynamic: false, - min_binding_size: wgpu::BufferSize::new(light_uniform_size), - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 2, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::SampledTexture { - multisampled: false, - component_type: wgpu::TextureComponentType::Float, - dimension: wgpu::TextureViewDimension::D2Array, - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 3, - visibility: wgpu::ShaderStage::FRAGMENT, - ty: wgpu::BindingType::Sampler { comparison: true }, - count: None, - }, - ], - label: None, - }); - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("main"), - bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout], - push_constant_ranges: &[], - }); - - let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); - let forward_uniforms = ForwardUniforms { - proj: *mx_total.as_ref(), - num_lights: [lights.len() as u32, 0, 0, 0], - }; - let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Uniform Buffer"), - contents: bytemuck::bytes_of(&forward_uniforms), - usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, - }); - - // Create bind group - let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - layout: &bind_group_layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::Buffer(light_uniform_buf.slice(..)), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(&shadow_view), - }, - wgpu::BindGroupEntry { - binding: 3, - resource: wgpu::BindingResource::Sampler(&shadow_sampler), - }, - ], - label: None, - }); - - // Create the render pipeline - let vs_module = device.create_shader_module(wgpu::include_spirv!("../resources/forward.vert.spv")); - let fs_module = device.create_shader_module(wgpu::include_spirv!("../resources/forward.frag.spv")); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("main"), - layout: Some(&pipeline_layout), - vertex_stage: wgpu::ProgrammableStageDescriptor { - module: &vs_module, - entry_point: "main", - }, - fragment_stage: Some(wgpu::ProgrammableStageDescriptor { - module: &fs_module, - entry_point: "main", - }), - rasterization_state: Some(wgpu::RasterizationStateDescriptor { - front_face: wgpu::FrontFace::Ccw, - cull_mode: wgpu::CullMode::Back, - ..Default::default() - }), - primitive_topology: wgpu::PrimitiveTopology::TriangleList, - color_states: &[sc_desc.format.into()], - depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { - format: Self::DEPTH_FORMAT, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilStateDescriptor::default(), - }), - vertex_state: wgpu::VertexStateDescriptor { - index_format: wgpu::IndexFormat::Uint16, - vertex_buffers: &[vb_desc], - }, - sample_count: 1, - sample_mask: !0, - alpha_to_coverage_enabled: false, - }); - - Pass { - pipeline, - bind_group, - uniform_buf, - } - }; + // Init + let mut runtime = runtime::Runtime::init(&sc_desc, &device, &queue); - let depth_texture = device.create_texture(&wgpu::TextureDescriptor { - size: wgpu::Extent3d { - width: sc_desc.width, - height: sc_desc.height, - depth: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: Self::DEPTH_FORMAT, - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - label: None, - }); - - Example { - entities, - lights, - lights_are_dirty: true, - shadow_pass, - forward_pass, - forward_depth: depth_texture.create_view(&wgpu::TextureViewDescriptor::default()), - light_uniform_buf, - } - } - - fn update(&mut self, _event: winit::event::WindowEvent) { - //empty - } - - fn resize( - &mut self, - sc_desc: &wgpu::SwapChainDescriptor, - device: &wgpu::Device, - queue: &wgpu::Queue, - ) { - // update view-projection matrix - let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); - let mx_ref: &[f32; 16] = mx_total.as_ref(); - queue.write_buffer( - &self.forward_pass.uniform_buf, - 0, - bytemuck::cast_slice(mx_ref), - ); - - let depth_texture = device.create_texture(&wgpu::TextureDescriptor { - size: wgpu::Extent3d { - width: sc_desc.width, - height: sc_desc.height, - depth: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: Self::DEPTH_FORMAT, - usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, - label: None, - }); - self.forward_depth = depth_texture.create_view(&wgpu::TextureViewDescriptor::default()); - } - - fn render( - &mut self, - frame: &wgpu::SwapChainTexture, - device: &wgpu::Device, - queue: &wgpu::Queue, - _spawner: &impl futures::task::LocalSpawn, - ) { - // update uniforms - for entity in self.entities.iter_mut() { - if entity.rotation_speed != 0.0 { - let rotation = cgmath::Matrix4::from_angle_x(cgmath::Deg(entity.rotation_speed)); - entity.mx_world = entity.mx_world * rotation; - } - let data = EntityUniforms { - model: entity.mx_world.into(), - color: [ - entity.color.r as f32, - entity.color.g as f32, - entity.color.b as f32, - entity.color.a as f32, - ], - }; - queue.write_buffer(&entity.uniform_buf, 0, bytemuck::bytes_of(&data)); - } + // Not sure why this is guarded, maybe we don't handle the event loop timing? + #[cfg(not(target_arch = "wasm32"))] + let mut last_update_inst = Instant::now(); + + + log::info!("Entering render loop..."); - if self.lights_are_dirty { - self.lights_are_dirty = false; - for (i, light) in self.lights.iter().enumerate() { - queue.write_buffer( - &self.light_uniform_buf, - (i * mem::size_of::()) as wgpu::BufferAddress, - bytemuck::bytes_of(&light.to_raw()), - ); - } - } - let mut encoder = - device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - - encoder.push_debug_group("shadow passes"); - for (i, light) in self.lights.iter().enumerate() { - encoder.push_debug_group(&format!( - "shadow pass {} (light at position {:?})", - i, light.pos - )); - - // The light uniform buffer already has the projection, - // let's just copy it over to the shadow uniform buffer. - encoder.copy_buffer_to_buffer( - &self.light_uniform_buf, - (i * mem::size_of::()) as wgpu::BufferAddress, - &self.shadow_pass.uniform_buf, - 0, - 64, - ); - - encoder.insert_debug_marker("render entities"); - { - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[], - depth_stencil_attachment: Some( - wgpu::RenderPassDepthStencilAttachmentDescriptor { - attachment: &light.target_view, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: true, - }), - stencil_ops: None, - }, - ), - }); - pass.set_pipeline(&self.shadow_pass.pipeline); - pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]); - - for entity in &self.entities { - pass.set_bind_group(1, &entity.bind_group, &[]); - pass.set_index_buffer(entity.index_buf.slice(..)); - pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); - pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); + // This is just an winit event loop + event_loop.run(move |event, _, control_flow| { + let _ = (&instance, &adapter); // force ownership by the closure (wtf??) + + // Override the control flow behaviour based on our system + *control_flow = if cfg!(feature = "metal-auto-capture") { + ControlFlow::Exit + } else { + #[cfg(not(target_arch = "wasm32"))] + { + // Artificially slows the loop rate to 10 millis + // This is called after redraw events cleared + ControlFlow::WaitUntil(Instant::now() + Duration::from_millis(10)) + } + #[cfg(target_arch = "wasm32")] + { + ControlFlow::Poll } + }; + + match event { + event::Event::MainEventsCleared => { + #[cfg(not(target_arch = "wasm32"))] + { + // ask for a redraw every 20 millis + if last_update_inst.elapsed() > Duration::from_millis(20) { + window.request_redraw(); + last_update_inst = Instant::now(); + } + + pool.run_until_stalled(); + } + + #[cfg(target_arch = "wasm32")] + window.request_redraw(); } - encoder.pop_debug_group(); - } - encoder.pop_debug_group(); + // Resizing will queue a request_redraw + event::Event::WindowEvent { + event: WindowEvent::Resized(size), + .. + } => { + log::info!("Resizing to {:?}", size); + sc_desc.width = size.width; + sc_desc.height = size.height; - // forward pass - encoder.push_debug_group("forward rendering pass"); - { - let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { - attachment: &frame.view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: true, + example.resize(&sc_desc, &device, &queue); + + swap_chain = device.create_swap_chain(&surface, &sc_desc); + } + event::Event::WindowEvent { event, .. } => match event { + WindowEvent::KeyboardInput { + input: + event::KeyboardInput { + virtual_keycode: Some(event::VirtualKeyCode::Escape), + state: event::ElementState::Pressed, + .. }, - }], - depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { - attachment: &self.forward_depth, - depth_ops: Some(wgpu::Operations { - load: wgpu::LoadOp::Clear(1.0), - store: false, - }), - stencil_ops: None, - }), - }); - pass.set_pipeline(&self.forward_pass.pipeline); - pass.set_bind_group(0, &self.forward_pass.bind_group, &[]); - - for entity in &self.entities { - pass.set_bind_group(1, &entity.bind_group, &[]); - pass.set_index_buffer(entity.index_buf.slice(..)); - pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); - pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); + .. + } + | WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + _ => { + example.update(event); + } + }, + event::Event::RedrawRequested(_) => { + let frame = match swap_chain.get_current_frame() { + Ok(frame) => frame, + Err(_) => { + swap_chain = device.create_swap_chain(&surface, &sc_desc); + swap_chain + .get_current_frame() + .expect("Failed to acquire next swap chain texture!") + } + }; + + example.render(&frame.output, &device, &queue, &spawner); } + _ => {} } - encoder.pop_debug_group(); + }); - queue.submit(iter::once(encoder.finish())); - } + //framework::run::("shadow"); } -fn main() { - framework::run::("shadow"); -} + + diff --git a/src/render.rs b/src/render.rs new file mode 100644 index 0000000..000e226 --- /dev/null +++ b/src/render.rs @@ -0,0 +1,551 @@ +use crate::{EntityUniforms, Pass}; +use bytemuck::{Pod, Zeroable}; +use bytemuck::__core::mem; +use wgpu::util::DeviceExt; +use std::rc::Rc; + +#[repr(C)] +#[derive(Clone, Copy)] +struct ForwardUniforms { + proj: [[f32; 4]; 4], + num_lights: [u32; 4], +} + +unsafe impl Pod for ForwardUniforms {} + +unsafe impl Zeroable for ForwardUniforms {} + +#[repr(C)] +#[derive(Clone, Copy)] +struct EntityUniforms { + model: [[f32; 4]; 4], + color: [f32; 4], +} + +unsafe impl Pod for EntityUniforms {} + +unsafe impl Zeroable for EntityUniforms {} + +#[repr(C)] +struct ShadowUniforms { + proj: [[f32; 4]; 4], +} + +struct Pass { + pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, + uniform_buf: wgpu::Buffer, +} + + +pub struct Renderer { + lights_are_dirty: bool, + shadow_pass: Pass, + forward_pass: Pass, + forward_depth: wgpu::TextureView, + light_uniform_buf: wgpu::Buffer, +} + +impl Renderer { + const MAX_LIGHTS: usize = 10; + const SHADOW_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; + const SHADOW_SIZE: wgpu::Extent3d = wgpu::Extent3d { + width: 512, + height: 512, + depth: Self::MAX_LIGHTS as u32, + }; + const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; + + fn generate_matrix(aspect_ratio: f32) -> cgmath::Matrix4 { + let mx_projection = cgmath::perspective(cgmath::Deg(45f32), aspect_ratio, 1.0, 20.0); + let mx_view = cgmath::Matrix4::look_at( + cgmath::Point3::new(3.0f32, -10.0, 6.0), + cgmath::Point3::new(0f32, 0.0, 0.0), + cgmath::Vector3::unit_z(), + ); + let mx_correction = framework::OPENGL_TO_WGPU_MATRIX; + mx_correction * mx_projection * mx_view + } +} + +impl Renderer { + + pub fn create_buffer(&mut self, device: &wgpu::Device) { + + // Creates the vertex and index buffers for the cube + let vertex_size = mem::size_of::(); + let (cube_vertex_data, cube_index_data) = import_mesh("/home/mrh/source/3d-min-viable-eng/resources/my_tree.obj"); + let cube_vertex_buf = Rc::new(device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Cubes Vertex Buffer"), + contents: bytemuck::cast_slice(&cube_vertex_data), + usage: wgpu::BufferUsage::VERTEX, + }, + )); + + let cube_index_buf = Rc::new(device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: Some("Cubes Index Buffer"), + contents: bytemuck::cast_slice(&cube_index_data), + usage: wgpu::BufferUsage::INDEX, + }, + )); + + // Creates the vertex and index buffers for the plane + let (plane_vertex_data, plane_index_data) = create_plane(7.0); + let plane_vertex_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Plane Vertex Buffer"), + contents: bytemuck::cast_slice(&plane_vertex_data), + usage: wgpu::BufferUsage::VERTEX, + }); + + let plane_index_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Plane Index Buffer"), + contents: bytemuck::cast_slice(&plane_index_data), + usage: wgpu::BufferUsage::INDEX, + }); + + // Creates the uniform for entities, which does the rotation and projection + let entity_uniform_size = mem::size_of::() as wgpu::BufferAddress; + let plane_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: entity_uniform_size, + usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + mapped_at_creation: false, + }); + } + + pub fn init(&mut self) -> Renderer { + + // Pre init the light uniform, with slots enough for MAX_LIGHTS + let light_uniform_size = + (Self::MAX_LIGHTS * mem::size_of::()) as wgpu::BufferAddress; + + let light_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: light_uniform_size, + usage: wgpu::BufferUsage::UNIFORM + | wgpu::BufferUsage::COPY_SRC + | wgpu::BufferUsage::COPY_DST, + mapped_at_creation: false, + }); + + // This seems way way way way easier than what I was doing in tracer + // Though the attr thing is still a macro. Which would cause issues if + // I wanted to get tricky with the 0,1 types + let vertex_attr = wgpu::vertex_attr_array![0 => Float4, 1 => Float4]; + let vb_desc = wgpu::VertexBufferDescriptor { + stride: vertex_size as wgpu::BufferAddress, + step_mode: wgpu::InputStepMode::Vertex, + attributes: &vertex_attr, + }; + + /* + There appear to be two passes required for shadows, the shadow pass, and the forward pass + Need to open this up in renderdoc and see what it's actually doing + */ + + let shadow_pass = { + let uniform_size = mem::size_of::() as wgpu::BufferAddress; + // Create pipeline layout + let bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, // global + visibility: wgpu::ShaderStage::VERTEX, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + min_binding_size: wgpu::BufferSize::new(uniform_size), + }, + count: None, + }], + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("shadow"), + bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout], + push_constant_ranges: &[], + }); + + let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: uniform_size, + usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + mapped_at_creation: false, + }); + + // Create bind group + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + }], + label: None, + }); + + // Create the render pipeline + let vs_module = device.create_shader_module(wgpu::include_spirv!("../resources/bake.vert.spv")); + let fs_module = device.create_shader_module(wgpu::include_spirv!("../resources/bake.frag.spv")); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("shadow"), + layout: Some(&pipeline_layout), + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + depth_bias: 2, // corresponds to bilinear filtering + depth_bias_slope_scale: 2.0, + depth_bias_clamp: 0.0, + clamp_depth: device.features().contains(wgpu::Features::DEPTH_CLAMPING), + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[], + depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { + format: Self::SHADOW_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::LessEqual, + stencil: wgpu::StencilStateDescriptor::default(), + }), + vertex_state: wgpu::VertexStateDescriptor { + index_format: wgpu::IndexFormat::Uint32, + vertex_buffers: &[vb_desc.clone()], + }, + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + Pass { + pipeline, + bind_group, + uniform_buf, + } + }; + + let forward_pass = { + // Create pipeline layout + let bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, // global + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + min_binding_size: wgpu::BufferSize::new(mem::size_of::< + ForwardUniforms, + >() + as _), + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, // lights + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + min_binding_size: wgpu::BufferSize::new(light_uniform_size), + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::SampledTexture { + multisampled: false, + component_type: wgpu::TextureComponentType::Float, + dimension: wgpu::TextureViewDimension::D2Array, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::Sampler { comparison: true }, + count: None, + }, + ], + label: None, + }); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("main"), + bind_group_layouts: &[&bind_group_layout, &local_bind_group_layout], + push_constant_ranges: &[], + }); + + let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); + let forward_uniforms = ForwardUniforms { + proj: *mx_total.as_ref(), + num_lights: [lights.len() as u32, 0, 0, 0], + }; + let uniform_buf = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Uniform Buffer"), + contents: bytemuck::bytes_of(&forward_uniforms), + usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + }); + + // Create bind group + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Buffer(light_uniform_buf.slice(..)), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(&shadow_view), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::Sampler(&shadow_sampler), + }, + ], + label: None, + }); + + // Create the render pipeline + let vs_module = device.create_shader_module(wgpu::include_spirv!("../resources/forward.vert.spv")); + let fs_module = device.create_shader_module(wgpu::include_spirv!("../resources/forward.frag.spv")); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("main"), + layout: Some(&pipeline_layout), + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::Back, + ..Default::default() + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[sc_desc.format.into()], + depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor { + format: Self::DEPTH_FORMAT, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilStateDescriptor::default(), + }), + vertex_state: wgpu::VertexStateDescriptor { + index_format: wgpu::IndexFormat::Uint32, + vertex_buffers: &[vb_desc], + }, + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + Pass { + pipeline, + bind_group, + uniform_buf, + } + }; + + let depth_texture = device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width: sc_desc.width, + height: sc_desc.height, + depth: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + label: None, + }); + + Renderer { + lights_are_dirty: false, + shadow_pass, + forward_pass, + forward_depth: depth_texture.create_view(&wgpu::TextureViewDescriptor::default()), + light_uniform_buf, + } + } + + pub fn render( + &mut self, + frame: &wgpu::SwapChainTexture, + device: &wgpu::Device, + queue: &wgpu::Queue, + _spawner: &impl futures::task::LocalSpawn, + ) + { + // update uniforms + for entity in self.entities.iter_mut() { + if entity.rotation_speed != 0.0 { + let rotation = cgmath::Matrix4::from_angle_x(cgmath::Deg(entity.rotation_speed)); + entity.mx_world = entity.mx_world * rotation; + } + let data = EntityUniforms { + model: entity.mx_world.into(), + color: [ + entity.color.r as f32, + entity.color.g as f32, + entity.color.b as f32, + entity.color.a as f32, + ], + }; + queue.write_buffer(&entity.uniform_buf, 0, bytemuck::bytes_of(&data)); + } + + if self.lights_are_dirty { + self.lights_are_dirty = false; + for (i, light) in self.lights.iter().enumerate() { + queue.write_buffer( + &self.light_uniform_buf, + (i * mem::size_of::()) as wgpu::BufferAddress, + bytemuck::bytes_of(&light.to_raw()), + ); + } + } + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.push_debug_group("shadow passes"); + for (i, light) in self.lights.iter().enumerate() { + encoder.push_debug_group(&format!( + "shadow pass {} (light at position {:?})", + i, light.pos + )); + + // The light uniform buffer already has the projection, + // let's just copy it over to the shadow uniform buffer. + encoder.copy_buffer_to_buffer( + &self.light_uniform_buf, + (i * mem::size_of::()) as wgpu::BufferAddress, + &self.shadow_pass.uniform_buf, + 0, + 64, + ); + + encoder.insert_debug_marker("render entities"); + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[], + depth_stencil_attachment: Some( + wgpu::RenderPassDepthStencilAttachmentDescriptor { + attachment: &light.target_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: None, + }, + ), + }); + pass.set_pipeline(&self.shadow_pass.pipeline); + pass.set_bind_group(0, &self.shadow_pass.bind_group, &[]); + + for entity in &self.entities { + pass.set_bind_group(1, &entity.bind_group, &[]); + pass.set_index_buffer(entity.index_buf.slice(..)); + pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); + pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); + } + } + + encoder.pop_debug_group(); + } + encoder.pop_debug_group(); + + // forward pass + encoder.push_debug_group("forward rendering pass"); + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: true, + }, + }], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { + attachment: &self.forward_depth, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: false, + }), + stencil_ops: None, + }), + }); + pass.set_pipeline(&self.forward_pass.pipeline); + pass.set_bind_group(0, &self.forward_pass.bind_group, &[]); + + for entity in &self.entities { + pass.set_bind_group(1, &entity.bind_group, &[]); + pass.set_index_buffer(entity.index_buf.slice(..)); + pass.set_vertex_buffer(0, entity.vertex_buf.slice(..)); + pass.draw_indexed(0..entity.index_count as u32, 0, 0..1); + } + } + encoder.pop_debug_group(); + + queue.submit(iter::once(encoder.finish())); + } + + pub fn optional_features() -> wgpu::Features { + wgpu::Features::DEPTH_CLAMPING + } + + pub fn resize( + &mut self, + sc_desc: &wgpu::SwapChainDescriptor, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) + { + // update view-projection matrix + let mx_total = Self::generate_matrix(sc_desc.width as f32 / sc_desc.height as f32); + let mx_ref: &[f32; 16] = mx_total.as_ref(); + queue.write_buffer( + &self.forward_pass.uniform_buf, + 0, + bytemuck::cast_slice(mx_ref), + ); + + let depth_texture = device.create_texture(&wgpu::TextureDescriptor { + size: wgpu::Extent3d { + width: sc_desc.width, + height: sc_desc.height, + depth: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::DEPTH_FORMAT, + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, + label: None, + }); + self.forward_depth = depth_texture.create_view(&wgpu::TextureViewDescriptor::default()); + } +} + diff --git a/src/runtime.rs b/src/runtime.rs new file mode 100644 index 0000000..c1a8826 --- /dev/null +++ b/src/runtime.rs @@ -0,0 +1,202 @@ +use crate::ShadowUniforms; +use bytemuck::__core::mem; +use std::rc::Rc; + + +struct Entity { + mx_world: cgmath::Matrix4, + rotation_speed: f32, + color: wgpu::Color, + vertex_buf: Rc, + index_buf: Rc, + index_count: usize, + bind_group: wgpu::BindGroup, + uniform_buf: wgpu::Buffer, +} + +pub struct Runtime { + entities: Vec, // This is going to be ECS'd + lights: Vec, // ECS +} + +impl Runtime { + pub fn init( + sc_desc: &wgpu::SwapChainDescriptor, + device: &wgpu::Device, + _queue: &wgpu::Queue, ) -> Self + { + + // https://sotrh.github.io/learn-wgpu/beginner/tutorial5-textures/#the-bindgroup + // It appears like bindgroups are + + // Defines the Uniform buffer for the Vertex and Fragment shaders + let local_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStage::VERTEX | wgpu::ShaderStage::FRAGMENT, + ty: wgpu::BindingType::UniformBuffer { + dynamic: false, + min_binding_size: wgpu::BufferSize::new( + mem::size_of::() as _ + ), + }, + count: None, + }], + label: None, + }); + + + let mut entities = vec![{ + use cgmath::SquareMatrix; + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &local_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer(plane_uniform_buf.slice(..)), + }], + label: None, + }); + Entity { + mx_world: cgmath::Matrix4::identity(), + rotation_speed: 0.0, + color: wgpu::Color::WHITE, + vertex_buf: Rc::new(plane_vertex_buf), + index_buf: Rc::new(plane_index_buf), + index_count: plane_index_data.len(), + bind_group, + uniform_buf: plane_uniform_buf, + } + }]; + + struct CubeDesc { + offset: cgmath::Vector3, + angle: f32, + scale: f32, + rotation: f32, + } + + let cube_descs = [ + CubeDesc { + offset: cgmath::vec3(-2.0, -2.0, 2.0), + angle: 10.0, + scale: 0.7, + rotation: 1.5, + }, + ]; + + + for cube in &cube_descs { + use cgmath::{Decomposed, Deg, InnerSpace, Quaternion, Rotation3}; + + let transform = Decomposed { + disp: cube.offset.clone(), + rot: Quaternion::from_axis_angle(cube.offset.normalize(), Deg(cube.angle)), + scale: cube.scale, + }; + let uniform_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: entity_uniform_size, + usage: wgpu::BufferUsage::UNIFORM | wgpu::BufferUsage::COPY_DST, + mapped_at_creation: false, + }); + entities.push(Entity { + mx_world: cgmath::Matrix4::from(transform), + rotation_speed: cube.rotation, + color: wgpu::Color::GREEN, + vertex_buf: Rc::clone(&cube_vertex_buf), + index_buf: Rc::clone(&cube_index_buf), + index_count: cube_index_data.len(), + bind_group: device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &local_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer(uniform_buf.slice(..)), + }], + label: None, + }), + uniform_buf, + }); + } + + // Create other resources + let shadow_sampler = device.create_sampler(&wgpu::SamplerDescriptor { + label: Some("shadow"), + address_mode_u: wgpu::AddressMode::ClampToEdge, + address_mode_v: wgpu::AddressMode::ClampToEdge, + address_mode_w: wgpu::AddressMode::ClampToEdge, + mag_filter: wgpu::FilterMode::Linear, + min_filter: wgpu::FilterMode::Linear, + mipmap_filter: wgpu::FilterMode::Nearest, + compare: Some(wgpu::CompareFunction::LessEqual), + ..Default::default() + }); + + let shadow_texture = device.create_texture(&wgpu::TextureDescriptor { + size: Self::SHADOW_SIZE, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: Self::SHADOW_FORMAT, + usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT | wgpu::TextureUsage::SAMPLED, + label: None, + }); + let shadow_view = shadow_texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let mut shadow_target_views = (0..2) + .map(|i| { + Some(shadow_texture.create_view(&wgpu::TextureViewDescriptor { + label: Some("shadow"), + format: None, + dimension: Some(wgpu::TextureViewDimension::D2), + aspect: wgpu::TextureAspect::All, + base_mip_level: 0, + level_count: None, + base_array_layer: i as u32, + array_layer_count: NonZeroU32::new(1), + })) + }) + .collect::>(); + + // This is just metadata we hold for the lights. We can hold onto this + let lights = vec![ + Light { + pos: cgmath::Point3::new(7.0, -5.0, 10.0), + color: wgpu::Color { + r: 0.5, + g: 1.0, + b: 0.5, + a: 1.0, + }, + fov: 60.0, + depth: 1.0..20.0, + target_view: shadow_target_views[0].take().unwrap(), + }, + Light { + pos: cgmath::Point3::new(-5.0, 7.0, 10.0), + color: wgpu::Color { + r: 1.0, + g: 0.5, + b: 0.5, + a: 1.0, + }, + fov: 45.0, + depth: 1.0..20.0, + target_view: shadow_target_views[1].take().unwrap(), + }, + ]; + + Runtime { + entities, + lights, + lights_are_dirty: true, + + light_uniform_buf, + } + } + + pub fn update(&mut self, _event: winit::event::WindowEvent) { + //empty + } +} \ No newline at end of file