diff --git a/CMakeLists.txt b/CMakeLists.txt index 978ce88..faf75c3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,8 @@ mark_as_advanced(SUBMODULE_SOURCES) if(BSF_AUTO_FETCH) message(STATUS "Fetching 'bsf' submodule...") if(NOT SUBMODULE_SOURCES) - execute_process(COMMAND git submodule update - --init + execute_process(COMMAND git submodule update + --init -- bsf WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) else() @@ -41,7 +41,7 @@ configure_file("${PROJECT_SOURCE_DIR}/Source/CMake/BsExampleConfig.h.in" "${PROJ # Check data dependencies check_and_update_builtin_assets(bsfExamples ${PROJECT_SOURCE_DIR}/Data Data ${BS_EXAMPLES_BUILTIN_ASSETS_VERSION} NO) - + # Sub-directories add_subdirectory(Source/Common) add_subdirectory(Source/LowLevelRendering) @@ -50,8 +50,9 @@ add_subdirectory(Source/CustomMaterials) add_subdirectory(Source/GUI) add_subdirectory(Source/Audio) add_subdirectory(Source/SkeletalAnimation) +add_subdirectory(Source/Asteroids) add_subdirectory(Source/Physics) add_subdirectory(Source/Particles) add_subdirectory(Source/Decals) add_subdirectory_optional(Source/Experimental/Shadows) -add_subdirectory_optional(Source/Experimental/Particles) \ No newline at end of file +add_subdirectory_optional(Source/Experimental/Particles) diff --git a/Source/Asteroids/CMakeLists.txt b/Source/Asteroids/CMakeLists.txt new file mode 100644 index 0000000..5feef46 --- /dev/null +++ b/Source/Asteroids/CMakeLists.txt @@ -0,0 +1,25 @@ +file(GLOB_RECURSE SRCS + *.cpp + *.c +) + +# Target +if(WIN32) + add_executable(Asteroids WIN32 "${SRCS}") +else() + add_executable(Asteroids "${SRCS}") +endif() + +# Working directory +set_target_properties(Asteroids PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$(OutDir)") + +# Libraries +## Local libs +target_link_libraries(Asteroids Common) + +# Plugin dependencies +add_engine_dependencies(Asteroids) +add_dependencies(Asteroids bsfFBXImporter bsfFontImporter bsfFreeImgImporter) + +# IDE specific +set_property(TARGET Asteroids PROPERTY FOLDER Examples) diff --git a/Source/Asteroids/CSpinner.cpp b/Source/Asteroids/CSpinner.cpp new file mode 100644 index 0000000..f84839e --- /dev/null +++ b/Source/Asteroids/CSpinner.cpp @@ -0,0 +1,33 @@ +#include "./CSpinner.h" +#include "Scene/BsSceneObject.h" +#include "Math/BsRandom.h" + +namespace bs +{ + +CSpinner::CSpinner(const HSceneObject& parent, Random* rand) : + Component(parent), + rotAxis(rand->getUnitVector()), + speed(rand->getUNorm() * 0.005 + 0.005) {} + +void CSpinner::fixedUpdate() { + SO()->rotate(rotAxis, Radian(speed)); +} + +COrbiter::COrbiter(const HSceneObject& parent, Random* rand) : + Component(parent), + speed(rand->getUNorm() * 0.0005 + 0.0005) {} + +void COrbiter::fixedUpdate() { + // SO()->rotate(Vector3::UNIT_Y, Radian(0.001)); + // Q() + // auto q = SO()->getTransform().getRotation(); + Quaternion rot; + rot.fromEulerAngles(Radian(0), Radian(speed), Radian(0)); + auto pos = SO()->getTransform().getPosition(); + // std::cout << "POS " << pos.x << " " << pos.y << std::endl; + pos = rot.rotate(pos); + SO()->setWorldPosition(pos); +} + +} diff --git a/Source/Asteroids/CSpinner.h b/Source/Asteroids/CSpinner.h new file mode 100644 index 0000000..caf5d7b --- /dev/null +++ b/Source/Asteroids/CSpinner.h @@ -0,0 +1,26 @@ +#include "Scene/BsComponent.h" + +namespace bs +{ + +// Spinning asteroid component. +class Random; +class CSpinner : public Component +{ + Vector3 rotAxis; + float speed; +public: + CSpinner(const HSceneObject& parent, Random* rand); + void fixedUpdate() override; +}; + +class COrbiter : public Component +{ +public: + float speed; + COrbiter(const HSceneObject& parent, Random* rand); + void fixedUpdate() override; +}; + + +} // namespace bs diff --git a/Source/Asteroids/Main.cpp b/Source/Asteroids/Main.cpp new file mode 100644 index 0000000..57e5a95 --- /dev/null +++ b/Source/Asteroids/Main.cpp @@ -0,0 +1,270 @@ +// Framework includes +#include "BsApplication.h" +#include "Resources/BsResources.h" +#include "Resources/BsBuiltinResources.h" +#include "Material/BsMaterial.h" +#include "Components/BsCCamera.h" +#include "Components/BsCRenderable.h" +#include "Components/BsCSkybox.h" +#include "Components/BsCPlaneCollider.h" +#include "Components/BsCBoxCollider.h" +#include "Components/BsCCharacterController.h" +#include "Components/BsCRigidbody.h" +#include "Physics/BsPhysicsMaterial.h" +#include "RenderAPI/BsRenderAPI.h" +#include "RenderAPI/BsRenderWindow.h" +#include "Scene/BsSceneObject.h" +#include "Input/BsInput.h" +#include "Components/BsCDecal.h" + +// Example includes +#include "BsExampleFramework.h" +#include "BsFPSWalker.h" +#include "BsFPSCamera.h" +#include "Math/BsRandom.h" + +#include "./mesh.h" +#include "./CSpinner.h" +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// This example sets up a simple environment consisting of a floor and cube, and a decal projecting on both surfaces. The +// example demonstrates how to set up decals, how decals are not shown on surfaces perpendicular to the decal direction, +// and optionally how to use masking to only project a decal onto a certain set of surfaces. +// +// It also sets up necessary physical objects for collision, as well as the character collider and necessary components +// for walking around the environment. +// +// The example first sets up the scene consisting of a floor, box and a skybox. Character controller is created next, +// as well as the camera. Components for moving the character controller and the camera are attached to allow the user to +// control the character. It then loads the required decal textures, sets up a decal material and initializes the actual +// decal component. Finally the cursor is hidden and quit on Esc key press hooked up. +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +namespace bs +{ + constexpr float GROUND_PLANE_SCALE = 50.0f; + + UINT32 windowResWidth = 1280; + UINT32 windowResHeight = 720; + + SPtr decal; + + void addCamera(HSceneObject characterSO) { + // In order something to render on screen we need at least one camera. + + // Like before, we create a new scene object at (0, 0, 0). + HSceneObject sceneCameraSO = SceneObject::create("SceneCamera"); + + // Get the primary render window we need for creating the camera. + SPtr window = gApplication().getPrimaryWindow(); + + // Add a Camera component that will output whatever it sees into that window + // (You could also use a render texture or another window you created). + HCamera sceneCamera = sceneCameraSO->addComponent(); + sceneCamera->getViewport()->setTarget(window); + + // Set up camera component properties + + // Set closest distance that is visible. Anything below that is clipped. + sceneCamera->setNearClipDistance(0.005f); + + // Set farthest distance that is visible. Anything above that is clipped. + sceneCamera->setFarClipDistance(1000); + + // Set aspect ratio depending on the current resolution + sceneCamera->setAspectRatio(windowResWidth / (float)windowResHeight); + + // Add a component that allows the camera to be rotated using the mouse + sceneCameraSO->setRotation(Quaternion(Degree(-10.0f), Degree(0.0f), Degree(0.0f))); + HFPSCamera fpsCamera = sceneCameraSO->addComponent(); + + // Set the character controller on the FPS camera, so the component can apply yaw rotation to it + fpsCamera->setCharacter(characterSO); + + // Make the camera a child of the character scene object, and position it roughly at eye level + sceneCameraSO->setParent(characterSO); + sceneCameraSO->setPosition(Vector3(0.0f, 1.8f * 0.5f - 0.1f, -2.0f)); + } + + void addFloor() { + /************************************************************************/ + /* ASSETS */ + /************************************************************************/ + + // Prepare all the resources we'll be using throughout this example + + // Grab a couple of test textures that we'll apply to the rendered objects + HTexture gridPattern = ExampleFramework::loadTexture(ExampleTexture::GridPattern); + HTexture gridPattern2 = ExampleFramework::loadTexture(ExampleTexture::GridPattern2); + // Grab the default PBR shader + HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard); + + // Create a set of materials to apply to renderables used + HMaterial planeMaterial = Material::create(shader); + planeMaterial->setTexture("gAlbedoTex", gridPattern2); + + // Tile the texture so every tile covers a 2x2m area + planeMaterial->setVec2("gUVTile", Vector2::ONE * GROUND_PLANE_SCALE * 0.5f); + + // Load meshes we'll used for our rendered objects + HMesh planeMesh = gBuiltinResources().getMesh(BuiltinMesh::Quad); + + // Set up renderable geometry for the floor plane + HSceneObject floorSO = SceneObject::create("Floor"); + HRenderable floorRenderable = floorSO->addComponent(); + floorRenderable->setMesh(planeMesh); + floorRenderable->setMaterial(planeMaterial); + + floorSO->setScale(Vector3(GROUND_PLANE_SCALE, 1.0f, GROUND_PLANE_SCALE)); + + // Add a plane collider that will prevent physical objects going through the floor + HPlaneCollider planeCollider = floorSO->addComponent(); + + } + + void addAsteroids() { + + // make asteroid meshes. + unsigned int meshCount = 100; + unsigned int subdivCount = 3; + + HShader shader = gBuiltinResources().getBuiltinShader(BuiltinShader::Standard); + HMaterial asteroidMaterial = Material::create(shader); + + std::vector meshes; + makeMeshes(meshCount, subdivCount, meshes); + assert(meshes.size() == meshCount); + + Random rand; + + for (unsigned int i = 0; i < meshes.size(); ++i) { + auto mesh = meshes[i]; + HSceneObject ast = SceneObject::create("Ast"); + HRenderable boxRenderable = ast->addComponent(); + ast->addComponent(&rand); + + boxRenderable->setMesh(mesh); + boxRenderable->setMaterial(asteroidMaterial); + + ast->setPosition(Vector3(i * 3, 1.2f, -10.5f)); + } + + unsigned int numInstances = 1000; // 10,000 + for (unsigned int i = 0; i < numInstances; ++i) { + auto mesh = meshes[i % meshCount]; + HSceneObject ast = SceneObject::create("RevolvingAst"); + HRenderable boxRenderable = ast->addComponent(); + ast->addComponent(&rand); + ast->addComponent(&rand); + + boxRenderable->setMesh(mesh); + boxRenderable->setMaterial(asteroidMaterial); + + float shellThickness = 0.5; // 10% outer sphere. + Vector3 point = rand.getPointInSphereShell(shellThickness); + point *= 100; + ast->setPosition(point); + } + + } + + /** Set up the scene used by the example, and the camera to view the world through. */ + void setUpScene() + { + + + addAsteroids(); + + /************************************************************************/ + /* FLOOR */ + /************************************************************************/ + + addFloor(); + + /************************************************************************/ + /* CHARACTER */ + /************************************************************************/ + + // Add physics geometry and components for character movement and physics interaction + HSceneObject characterSO = SceneObject::create("Character"); + characterSO->setPosition(Vector3(0.0f, 1.0f, 5.0f)); + + // Add a character controller, representing the physical geometry of the character + HCharacterController charController = characterSO->addComponent(); + + // Make the character about 1.8m high, with 0.4m radius (controller represents a capsule) + charController->setHeight(1.0f); // + 0.4 * 2 radius = 1.8m height + charController->setRadius(0.4f); + + // FPS walker uses default input controls to move the character controller attached to the same object + characterSO->addComponent(); + + /************************************************************************/ + /* CAMERA */ + /************************************************************************/ + + addCamera(characterSO); + + /************************************************************************/ + /* SKYBOX */ + /************************************************************************/ + + // Load a skybox texture + HTexture skyCubemap = ExampleFramework::loadTexture(ExampleTexture::EnvironmentRathaus, false, true, true); + + // Add a skybox texture for sky reflections + HSceneObject skyboxSO = SceneObject::create("Skybox"); + + HSkybox skybox = skyboxSO->addComponent(); + skybox->setTexture(skyCubemap); + + /************************************************************************/ + /* INPUT */ + /************************************************************************/ + + // Hook up input that launches a sphere when user clicks the mouse, and Esc key to quit + gInput().onButtonUp.connect([=](const ButtonEvent& ev) + { + if(ev.buttonCode == BC_ESCAPE) + { + // Quit the application when Escape key is pressed + gApplication().quitRequested(); + } + }); + + } +} + +/** Main entry point into the application. */ +#if BS_PLATFORM == BS_PLATFORM_WIN32 +#include + +int CALLBACK WinMain( + _In_ HINSTANCE hInstance, + _In_ HINSTANCE hPrevInstance, + _In_ LPSTR lpCmdLine, + _In_ int nCmdShow + ) +#else +int main() +#endif +{ + using namespace bs; + + // Initializes the application and creates a window with the specified properties + VideoMode videoMode(windowResWidth, windowResHeight); + Application::startUp(videoMode, "Example", false); + + // Registers a default set of input controls + ExampleFramework::setupInputConfig(); + + // Set up the scene with an object to render and a camera + setUpScene(); + + // Runs the main loop that does most of the work. This method will exit when user closes the main + // window or exits in some other way. + Application::instance().runMainLoop(); + + // When done, clean up + Application::shutDown(); + + return 0; +} diff --git a/Source/Asteroids/license.txt b/Source/Asteroids/license.txt new file mode 100644 index 0000000..de6cd06 --- /dev/null +++ b/Source/Asteroids/license.txt @@ -0,0 +1,181 @@ + +Apache License + Version 2.0, January 2004 + + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this +License, each Contributor hereby grants to You a perpetual, worldwide, +non-exclusive, no-charge, royalty-free, irrevocable copyright license to +reproduce, prepare Derivative Works of, publicly display, publicly perform, +sublicense, and distribute the Work and such Derivative Works in Source or +Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, +each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, +no-charge, royalty-free, irrevocable (except as stated in this section) patent +license to make, have made, use, offer to sell, sell, import, and otherwise +transfer the Work, where such license applies only to those patent claims +licensable by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) with the Work +to which such Contribution(s) was submitted. If You institute patent litigation +against any entity (including a cross-claim or counterclaim in a lawsuit) +alleging that the Work or a Contribution incorporated within the Work +constitutes direct or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate as of the date +such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or +Derivative Works thereof in any medium, with or without modifications, and in +Source or Object form, provided that You meet the following conditions: + You must give any other recipients of the Work or Derivative Works a copy of + this License; and + + + You must cause any modified files to carry prominent notices stating that You + changed the files; and + + + You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from the + Source form of the Work, excluding those notices that do not pertain to any + part of the Derivative Works; and + + + If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those notices + that do not pertain to any part of the Derivative Works, in at least one of + the following places: within a NOTICE text file distributed as part of the + Derivative Works; within the Source form or documentation, if provided along + with the Derivative Works; or, within a display generated by the Derivative + Works, if and wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and do not modify the + License. You may add Your own attribution notices within Derivative Works that + You distribute, alongside or as an addendum to the NOTICE text from the Work, + provided that such additional attribution notices cannot be construed as + modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any +Contribution intentionally submitted for inclusion in the Work by You to the +Licensor shall be under the terms and conditions of this License, without any +additional terms or conditions. Notwithstanding the above, nothing herein shall +supersede or modify the terms of any separate license agreement you may have +executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, +trademarks, service marks, or product names of the Licensor, except as required +for reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in +writing, Licensor provides the Work (and each Contributor provides its +Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied, including, without limitation, any warranties +or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any risks +associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in +tort (including negligence), contract, or otherwise, unless required by +applicable law (such as deliberate and grossly negligent acts) or agreed to in +writing, shall any Contributor be liable to You for damages, including any +direct, indirect, special, incidental, or consequential damages of any character +arising as a result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, work stoppage, +computer failure or malfunction, or any and all other commercial damages or +losses), even if such Contributor has been advised of the possibility of such +damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or +Derivative Works thereof, You may choose to offer, and charge a fee for, +acceptance of support, warranty, indemnity, or other liability obligations +and/or rights consistent with this License. However, in accepting such +obligations, You may act only on Your own behalf and on Your sole +responsibility, not on behalf of any other Contributor, and only if You agree to +indemnify, defend, and hold each Contributor harmless for any liability incurred +by, or claims asserted against, such Contributor by reason of your accepting any +such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + +Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, +Version 2.0 (the "License"); you may not use this file except in compliance with +the License. You may obtain a copy of the License at +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or +agreed to in writing, software distributed under the License is distributed on +an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express +or implied. See the License for the specific language governing permissions and +limitations under the License. diff --git a/Source/Asteroids/mesh.cpp b/Source/Asteroids/mesh.cpp new file mode 100644 index 0000000..60d5cbf --- /dev/null +++ b/Source/Asteroids/mesh.cpp @@ -0,0 +1,485 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +/////////////////////////////////////////////////////////////////////////////// + +#include "mesh.h" +#include "noise.h" +#include +#include +#include + +#include "Mesh/BsMesh.h" +#include "RenderAPI/BsVertexDataDesc.h" +#include "Math/BsVector2.h" +#include "Mesh/BsMeshUtility.h" + +typedef bs::UINT32 IndexType; + +typedef bs::Vector3 Vertex; +typedef bs::Vector3 Normal; + +using bs::Vector2; + +namespace demo { + +// typedef unsigned short IndexType; + +// NOTE: This data could be compressed, but it's not really the bottleneck at the moment +// struct Vertex +// { +// float x; +// float y; +// float z; +// }; + + +// struct Normal +// { +// float nx; +// float ny; +// float nz; +// }; + +struct Mesh +{ + void clear() + { + normals.clear(); + vertices.clear(); + indices.clear(); + } + + std::vector vertices; + std::vector normals; + std::vector indices; + std::vector uv; +}; + + +void CreateIcosahedron(Mesh *outMesh); + +// 1 face -> 4 faces +void SubdivideInPlace(Mesh *outMesh); + +void SpherifyInPlace(Mesh *outMesh, float radius = 1.0f); + +void ComputeAvgNormalsInPlace(Mesh *outMesh); + +void CreateUVMap(Mesh *outMesh); + +// subdivIndexOffset array should be [subdivLevels+2] in size +void CreateGeospheres(Mesh *outMesh, unsigned int subdivLevelCount, unsigned int* outSubdivIndexOffsets); + + +void CreateIcosahedron(Mesh *outMesh) +{ + static const float a = std::sqrt(2.0f / (5.0f - std::sqrt(5.0f))); + static const float b = std::sqrt(2.0f / (5.0f + std::sqrt(5.0f))); + + static const size_t num_vertices = 12; + static const Vertex vertices[num_vertices] = // x, y, z + { + {-b, a, 0}, + { b, a, 0}, + {-b, -a, 0}, + { b, -a, 0}, + { 0, -b, a}, + { 0, b, a}, + { 0, -b, -a}, + { 0, b, -a}, + { a, 0, -b}, + { a, 0, b}, + {-a, 0, -b}, + {-a, 0, b}, + }; + + static const size_t num_triangles = 20; + static const IndexType indices[num_triangles*3] = + { + 0, 5, 11, + 0, 1, 5, + 0, 7, 1, + 0, 10, 7, + 0, 11, 10, + 1, 9, 5, + 5, 4, 11, + 11, 2, 10, + 10, 6, 7, + 7, 8, 1, + 3, 4, 9, + 3, 2, 4, + 3, 6, 2, + 3, 8, 6, + 3, 9, 8, + 4, 5, 9, + 2, 11, 4, + 6, 10, 2, + 8, 7, 6, + 9, 1, 8, + }; + + outMesh->clear(); + outMesh->vertices.insert(outMesh->vertices.end(), vertices, vertices + num_vertices); + outMesh->indices.insert(outMesh->indices.end(), indices, indices + num_triangles*3); + outMesh->normals.resize(outMesh->vertices.size()); +} + + +// Maps edge (lower index first!) to +struct Edge +{ + Edge(IndexType i0, IndexType i1) + : v0(i0), v1(i1) + { + if (v0 > v1) + std::swap(v0, v1); + } + IndexType v0; + IndexType v1; + + bool operator<(const Edge &c) const + { + return v0 < c.v0 || (v0 == c.v0 && v1 < c.v1); + } +}; + +typedef std::map MidpointMap; + +inline IndexType EdgeMidpoint(Mesh *mesh, MidpointMap *midpoints, Edge e) +{ + auto index = midpoints->find(e); + if (index == midpoints->end()) + { + auto a = mesh->vertices[e.v0]; + auto b = mesh->vertices[e.v1]; + + Vertex m; + m.x = (a.x + b.x) * 0.5f; + m.y = (a.y + b.y) * 0.5f; + m.z = (a.z + b.z) * 0.5f; + + index = midpoints->insert(std::make_pair(e, static_cast(mesh->vertices.size()))).first; + mesh->vertices.push_back(m); + } + return index->second; +} + + +void SubdivideInPlace(Mesh *outMesh) +{ + MidpointMap midpoints; + + std::vector newIndices; + newIndices.reserve(outMesh->indices.size() * 4); + outMesh->vertices.reserve(outMesh->vertices.size() * 2); + + assert(outMesh->indices.size() % 3 == 0); // trilist + size_t triangles = outMesh->indices.size() / 3; + for (size_t t = 0; t < triangles; ++t) + { + auto t0 = outMesh->indices[t*3+0]; + auto t1 = outMesh->indices[t*3+1]; + auto t2 = outMesh->indices[t*3+2]; + + auto m0 = EdgeMidpoint(outMesh, &midpoints, Edge(t0, t1)); + auto m1 = EdgeMidpoint(outMesh, &midpoints, Edge(t1, t2)); + auto m2 = EdgeMidpoint(outMesh, &midpoints, Edge(t2, t0)); + + IndexType indices[] = { + t0, m0, m2, + m0, t1, m1, + m0, m1, m2, + m2, m1, t2, + }; + newIndices.insert(newIndices.end(), indices, indices + 4*3); + } + + std::swap(outMesh->indices, newIndices); // Constant time +} + + +void SpherifyInPlace(Mesh *outMesh, float radius) +{ + for (auto &v : outMesh->vertices) { + float n = radius / std::sqrt(v.x*v.x + v.y*v.y + v.z*v.z); + v.x *= n; + v.y *= n; + v.z *= n; + } +} + + +void ComputeAvgNormalsInPlace(Mesh *outMesh) +{ + for (auto &v : outMesh->normals) { + v.x = 0.0f; + v.y = 0.0f; + v.z = 0.0f; + } + + assert(outMesh->indices.size() % 3 == 0); // trilist + size_t triangles = outMesh->indices.size() / 3; + for (size_t t = 0; t < triangles; ++t) + { + auto v1 = &outMesh->vertices[outMesh->indices[t*3+0]]; + auto v2 = &outMesh->vertices[outMesh->indices[t*3+1]]; + auto v3 = &outMesh->vertices[outMesh->indices[t*3+2]]; + + auto n1 = &outMesh->normals[outMesh->indices[t*3+0]]; + auto n2 = &outMesh->normals[outMesh->indices[t*3+1]]; + auto n3 = &outMesh->normals[outMesh->indices[t*3+2]]; + + // Two edge vectors u,v + auto ux = v2->x - v1->x; + auto uy = v2->y - v1->y; + auto uz = v2->z - v1->z; + auto vx = v3->x - v1->x; + auto vy = v3->y - v1->y; + auto vz = v3->z - v1->z; + + // cross(u,v) + float nx = uy*vz - uz*vy; + float ny = uz*vx - ux*vz; + float nz = ux*vy - uy*vx; + + // Do not normalize... weight average by contributing face area + n1->x += nx; n1->y += ny; n1->z += nz; + n2->x += nx; n2->y += ny; n2->z += nz; + n3->x += nx; n3->y += ny; n3->z += nz; + } + + // Normalize + for (auto &v : outMesh->normals) { + float n = 1.0f / std::sqrt(v.x*v.x + v.y*v.y + v.z*v.z); + v.x *= n; + v.y *= n; + v.z *= n; + } +} + + +void CreateGeospheres(Mesh *outMesh, unsigned int subdivLevelCount, unsigned int* outSubdivIndexOffsets) +{ + CreateIcosahedron(outMesh); + outSubdivIndexOffsets[0] = 0; + + std::vector vertices(outMesh->vertices); + std::vector normals(outMesh->normals); + std::vector indices(outMesh->indices); + + for (unsigned int i = 0; i < subdivLevelCount; ++i) { + outSubdivIndexOffsets[i+1] = (unsigned int)indices.size(); + SubdivideInPlace(outMesh); + + // Ensure we add the proper offset to the indices from this subdiv level for the combined mesh + // This avoids also needing to track a base vertex index for each subdiv level + IndexType vertexOffset = (IndexType)vertices.size(); + vertices.insert(vertices.end(), outMesh->vertices.begin(), outMesh->vertices.end()); + normals.resize(vertices.size()); + + for (auto newIndex : outMesh->indices) { + indices.push_back(newIndex + vertexOffset); + } + } + outSubdivIndexOffsets[subdivLevelCount+1] = (unsigned int)indices.size(); + + SpherifyInPlace(outMesh); + + // Put the union of vertices/indices back into the mesh object + std::swap(outMesh->indices, indices); + std::swap(outMesh->vertices, vertices); + std::swap(outMesh->normals, normals); +} + + +void CreateUVMap(Mesh* outMesh) { + std::vector uvs(outMesh->vertices.size()); + auto size = outMesh->vertices.size(); + for (unsigned int i = 0; i < size; ++i) { + uvs[i] = {outMesh->vertices[i].x, outMesh->vertices[i].y}; + } + std::swap(outMesh->uv, uvs); + + // make tangents as well + +} + +void CreateAsteroidsFromGeospheres(Mesh *outMesh, + unsigned int subdivLevelCount, unsigned int meshInstanceCount, + unsigned int rngSeed, + unsigned int* outSubdivIndexOffsets, unsigned int* vertexCountPerMesh) +{ + assert(subdivLevelCount <= meshInstanceCount); + + std::mt19937 rng(rngSeed); + + Mesh baseMesh; + CreateGeospheres(&baseMesh, subdivLevelCount, outSubdivIndexOffsets); + + // Per unique mesh + *vertexCountPerMesh = (unsigned int)baseMesh.vertices.size(); + std::vector vertices; + vertices.reserve(meshInstanceCount * baseMesh.vertices.size()); + std::vector normals; + normals.reserve(meshInstanceCount * baseMesh.normals.size()); + // Reuse indices for the different unique meshes + + auto randomNoise = std::uniform_real_distribution(0.0f, 10000.0f); + auto randomPersistence = std::normal_distribution(0.95f, 0.04f); + float noiseScale = 0.5f; + float radiusScale = 0.9f; + float radiusBias = 0.3f; + + // Create and randomize unique vertices for each mesh instance + for (unsigned int m = 0; m < meshInstanceCount; ++m) { + Mesh newMesh(baseMesh); + NoiseOctaves<4> textureNoise(randomPersistence(rng)); + float noise = randomNoise(rng); + + for (auto &v : newMesh.vertices) { + float radius = textureNoise(v.x*noiseScale, v.y*noiseScale, v.z*noiseScale, noise); + radius = radius * radiusScale + radiusBias; + v.x *= radius; + v.y *= radius; + v.z *= radius; + } + ComputeAvgNormalsInPlace(&newMesh); + + vertices.insert(vertices.end(), newMesh.vertices.begin(), newMesh.vertices.end()); + normals.insert(normals.end(), newMesh.normals.begin(), newMesh.normals.end()); + } + + // Copy to output + std::swap(outMesh->indices, baseMesh.indices); + std::swap(outMesh->vertices, vertices); + std::swap(outMesh->normals, normals); + + // Quick hack to create UV map. + CreateUVMap(outMesh); +} + +} // namespace demo + +namespace bs { + +void generateTangents(UINT8* positions, UINT8* normals, UINT8* uv, UINT32* indices, + UINT32 numVertices, UINT32 numIndices, UINT32 vertexOffset, UINT32 indexOffset, UINT32 vertexStride, UINT8* tangents) +{ + Vector3* tempTangents = bs_stack_alloc(numVertices); + Vector3* tempBitangents = bs_stack_alloc(numVertices); + + MeshUtility::calculateTangents( + (Vector3*)(positions + vertexOffset * vertexStride), + (Vector3*)(normals + vertexOffset * vertexStride), + (Vector2*)(uv + vertexOffset * vertexStride), + (UINT8*)(indices + indexOffset), + numVertices, numIndices, tempTangents, tempBitangents, 4, vertexStride); + + for (UINT32 i = 0; i < (UINT32)numVertices; i++) + { + Vector3 normal = *(Vector3*)&normals[(vertexOffset + i) * vertexStride]; + Vector3 tangent = tempTangents[i]; + Vector3 bitangent = tempBitangents[i]; + + Vector3 engineBitangent = Vector3::cross(normal, tangent); + float sign = Vector3::dot(engineBitangent, bitangent); + + Vector4 packedTangent(tangent.x, tangent.y, tangent.z, sign > 0 ? 1.0f : -1.0f); + memcpy(tangents + (vertexOffset + i) * vertexStride, &packedTangent, sizeof(Vector4)); + } + + bs_stack_free(tempBitangents); + bs_stack_free(tempTangents); +} + +void CalculateTangents(SPtr meshData) { + const SPtr& desc = meshData->getVertexDesc(); + UINT32* indexData = meshData->getIndices32(); + UINT8* positionData = meshData->getElementData(VES_POSITION); + UINT8* normalData = meshData->getElementData(VES_NORMAL); + + UINT32 numVertices = meshData->getNumVertices(); + UINT32 numIndices = meshData->getNumIndices(); + UINT32 vertexStride = desc->getVertexStride(); + + unsigned int vertexOffset = 0; + unsigned int indexOffset = 0; + UINT8* uvData = meshData->getElementData(VES_TEXCOORD); + UINT8* tangentData = meshData->getElementData(VES_TANGENT); + generateTangents(positionData, normalData, uvData, indexData, numVertices, numIndices, + vertexOffset, indexOffset, vertexStride, tangentData); +} + +void makeMeshes(unsigned int meshInstanceCount, unsigned int subdivCount, std::vector& meshes) { + + + std::vector mIndexOffsets(subdivCount + 2); + unsigned int mSubdivCount(subdivCount); + unsigned int mVertexCountPerMesh{0}; + demo::Mesh _meshes; // one big mesh with lots of sub-meshes... + unsigned int rng = 100; + demo::CreateAsteroidsFromGeospheres(&_meshes, mSubdivCount, meshInstanceCount, rng, mIndexOffsets.data(), &mVertexCountPerMesh); + assert(_meshes.vertices.size() == _meshes.normals.size()); + + // std::cout << "meshInstanceCount " << meshInstanceCount << " " << subdivCount << " " << mIndexOffsets.size() << " " << mVertexCountPerMesh << std::endl; + // std::cout << "vertices " << _meshes.vertices.size() << " " << _meshes.indices.size() << std::endl; + + // for (auto i : mIndexOffsets) { + // std::cout << i << " "; + // } + // std::cout << std::endl; + + + SPtr vertexDesc = VertexDataDesc::create(); + vertexDesc->addVertElem(VET_FLOAT3, VES_POSITION); + vertexDesc->addVertElem(VET_FLOAT3, VES_NORMAL); + vertexDesc->addVertElem(VET_FLOAT3, VES_TANGENT); + vertexDesc->addVertElem(VET_FLOAT2, VES_TEXCOORD); + + for (unsigned int i = 0; i < meshInstanceCount; ++i) { + MESH_DESC meshDesc; + + unsigned int j = subdivCount; + unsigned int vertexOffset = i * mVertexCountPerMesh; + meshDesc.vertexDesc = vertexDesc; + meshDesc.numVertices = mVertexCountPerMesh; + meshDesc.numIndices = mIndexOffsets[j + 1] - mIndexOffsets[j]; + + SPtr meshData = MeshData::create(meshDesc.numVertices, meshDesc.numIndices, vertexDesc); + // Write the vertices + meshData->setVertexData(VES_POSITION, &_meshes.vertices[vertexOffset], sizeof(Vertex) * meshDesc.numVertices); + meshData->setVertexData(VES_NORMAL, &_meshes.normals[vertexOffset], sizeof(Vertex) * meshDesc.numVertices); + meshData->setVertexData(VES_TEXCOORD, &_meshes.uv[vertexOffset], sizeof(Vector2) * meshDesc.numVertices); + + // meshData->setIndexData(VES_NORMAL, _meshes.indices[0], sizeof(IndexType) * meshDesc.numIndices); + memcpy(meshData->getIndices32(), &_meshes.indices[mIndexOffsets[j]], sizeof(IndexType) * meshDesc.numIndices); + + CalculateTangents(meshData); + // auto bounds = meshData->calculateBounds(); + + // THIS DOESN'T WORK? IS THIS A DOCS ISSUE?? + // bool discard = false; + // worth investigating.... + // HMesh mesh = Mesh::create(meshDesc); + // mesh->writeData(meshData, discard); + // but initializing with meshData does work... + HMesh mesh = Mesh::create(meshData); + + meshes.push_back(mesh); + } + // std::cout << "FINISH " << std::endl; +} + +} diff --git a/Source/Asteroids/mesh.h b/Source/Asteroids/mesh.h new file mode 100644 index 0000000..9e1379f --- /dev/null +++ b/Source/Asteroids/mesh.h @@ -0,0 +1,27 @@ +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include +#include "BsCorePrerequisites.h" + +// original see https://github.com/GameTechDev/asteroids_d3d12/ + +namespace bs { + +void makeMeshes(unsigned int meshInstanceCount, unsigned int subdivCount, std::vector& meshes); +} diff --git a/Source/Asteroids/noise.h b/Source/Asteroids/noise.h new file mode 100644 index 0000000..e22e9e1 --- /dev/null +++ b/Source/Asteroids/noise.h @@ -0,0 +1,64 @@ + +/////////////////////////////////////////////////////////////////////////////// +// Copyright 2017 Intel Corporation +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. +/////////////////////////////////////////////////////////////////////////////// + +#pragma once + +#include "simplexnoise1234.h" + +// Very simple multi-octave simplex noise helper +// Returns noise in the range [0, 1] vs. the usual [-1, 1] +template +class NoiseOctaves +{ +private: + float mWeights[N]; + float mWeightNorm; + +public: + NoiseOctaves(float persistence = 0.5f) + { + float weightSum = 0.0f; + for (size_t i = 0; i < N; ++i) { + mWeights[i] = persistence; + weightSum += persistence; + persistence *= persistence; + } + mWeightNorm = 0.5f / weightSum; // Will normalize to [-0.5, 0.5] + } + + // Returns [0, 1] + float operator()(float x, float y, float z) const + { + float r = 0.0f; + for (size_t i = 0; i < N; ++i) { + r += mWeights[i] * snoise3(x, y, z); + x *= 2.0f; y *= 2.0f; z *= 2.0f; + } + return r * mWeightNorm + 0.5f; + } + + // Returns [0, 1] + float operator()(float x, float y, float z, float w) const + { + float r = 0.0f; + for (size_t i = 0; i < N; ++i) { + r += mWeights[i] * snoise4(x, y, z, w); + x *= 2.0f; y *= 2.0f; z *= 2.0f; w *= 2.0f; + } + return r * mWeightNorm + 0.5f; + } +}; diff --git a/Source/Asteroids/simplexnoise.c b/Source/Asteroids/simplexnoise.c new file mode 100644 index 0000000..20fc847 --- /dev/null +++ b/Source/Asteroids/simplexnoise.c @@ -0,0 +1,495 @@ +/* SimplexNoise1234, Simplex noise with true analytic + * derivative in 1D to 4D. + * + * Author: Stefan Gustavson, 2003-2005 + * Contact: stegu@itn.liu.se + */ + + /* +This code was GPL licensed until February 2011. +As the original author of this code, I hereby +release it irrevocably into the public domain. +Please feel free to use it for whatever you want. +Credit is appreciated where appropriate, and I also +appreciate being told where this code finds any use, +but you may do as you like. Alternatively, if you want +to have a familiar OSI-approved license, you may use +This code under the terms of the MIT license: +Copyright (C) 2003-2005 by Stefan Gustavson. All rights reserved. +This code is licensed to you under the terms of the MIT license: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +/** \file + \brief C implementation of Perlin Simplex Noise over 1,2,3, and 4 dimensions. + \author Stefan Gustavson (stegu@itn.liu.se) +*/ + +/* + * This implementation is "Simplex Noise" as presented by + * Ken Perlin at a relatively obscure and not often cited course + * session "Real-Time Shading" at Siggraph 2001 (before real + * time shading actually took on), under the title "hardware noise". + * The 3D function is numerically equivalent to his Java reference + * code available in the PDF course notes, although I re-implemented + * it from scratch to get more readable code. The 1D, 2D and 4D cases + * were implemented from scratch by me from Ken Perlin's text. + * + * This file has no dependencies on any other file, not even its own + * header file. The header file is made for use by external code only. + */ + + +// We don't need to include this. It does no harm, but no use either. +#include "simplexnoise1234.h" + +#pragma warning(disable: 4244) // conversion double -> float + +#define FASTFLOOR(x) ( ((x)>0) ? ((int)x) : (((int)x)-1) ) + +//--------------------------------------------------------------------- +// Static data + +/* + * Permutation table. This is just a random jumble of all numbers 0-255, + * repeated twice to avoid wrapping the index at 255 for each lookup. + * This needs to be exactly the same for all instances on all platforms, + * so it's easiest to just keep it as static explicit data. + * This also removes the need for any initialisation of this class. + * + * Note that making this an int[] instead of a char[] might make the + * code run faster on platforms with a high penalty for unaligned single + * byte addressing. Intel x86 is generally single-byte-friendly, but + * some other CPUs are faster with 4-aligned reads. + * However, a char[] is smaller, which avoids cache trashing, and that + * is probably the most important aspect on most architectures. + * This array is accessed a *lot* by the noise functions. + * A vector-valued noise over 3D accesses it 96 times, and a + * float-valued 4D noise 64 times. We want this to fit in the cache! + */ +unsigned char perm[512] = {151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180, + 151,160,137,91,90,15, + 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, + 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, + 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, + 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, + 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, + 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, + 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, + 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, + 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, + 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, + 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, + 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 +}; + +//--------------------------------------------------------------------- + +/* + * Helper functions to compute gradients-dot-residualvectors (1D to 4D) + * Note that these generate gradients of more than unit length. To make + * a close match with the value range of classic Perlin noise, the final + * noise values need to be rescaled to fit nicely within [-1,1]. + * (The simplex noise functions as such also have different scaling.) + * Note also that these noise functions are the most practical and useful + * signed version of Perlin noise. To return values according to the + * RenderMan specification from the SL noise() and pnoise() functions, + * the noise values need to be scaled and offset to [0,1], like this: + * float SLnoise = (noise(x,y,z) + 1.0) * 0.5; + */ + +float grad1( int hash, float x ) { + int h = hash & 15; + float grad = 1.0f + (h & 7); // Gradient value 1.0, 2.0, ..., 8.0 + if (h&8) grad = -grad; // Set a random sign for the gradient + return ( grad * x ); // Multiply the gradient with the distance +} + +float grad2( int hash, float x, float y ) { + int h = hash & 7; // Convert low 3 bits of hash code + float u = h<4 ? x : y; // into 8 simple gradient directions, + float v = h<4 ? y : x; // and compute the dot product with (x,y). + return ((h&1)? -u : u) + ((h&2)? -2.0f*v : 2.0f*v); +} + +float grad3( int hash, float x, float y , float z ) { + int h = hash & 15; // Convert low 4 bits of hash code into 12 simple + float u = h<8 ? x : y; // gradient directions, and compute dot product. + float v = h<4 ? y : h==12||h==14 ? x : z; // Fix repeats at h = 12 to 15 + return ((h&1)? -u : u) + ((h&2)? -v : v); +} + +float grad4( int hash, float x, float y, float z, float t ) { + int h = hash & 31; // Convert low 5 bits of hash code into 32 simple + float u = h<24 ? x : y; // gradient directions, and compute dot product. + float v = h<16 ? y : z; + float w = h<8 ? z : t; + return ((h&1)? -u : u) + ((h&2)? -v : v) + ((h&4)? -w : w); +} + + // A lookup table to traverse the simplex around a given point in 4D. + // Details can be found where this table is used, in the 4D noise method. + /* TODO: This should not be required, backport it from Bill's GLSL code! */ + static unsigned char simplex[64][4] = { + {0,1,2,3},{0,1,3,2},{0,0,0,0},{0,2,3,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,2,3,0}, + {0,2,1,3},{0,0,0,0},{0,3,1,2},{0,3,2,1},{0,0,0,0},{0,0,0,0},{0,0,0,0},{1,3,2,0}, + {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, + {1,2,0,3},{0,0,0,0},{1,3,0,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,3,0,1},{2,3,1,0}, + {1,0,2,3},{1,0,3,2},{0,0,0,0},{0,0,0,0},{0,0,0,0},{2,0,3,1},{0,0,0,0},{2,1,3,0}, + {0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0},{0,0,0,0}, + {2,0,1,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,0,1,2},{3,0,2,1},{0,0,0,0},{3,1,2,0}, + {2,1,0,3},{0,0,0,0},{0,0,0,0},{0,0,0,0},{3,1,0,2},{0,0,0,0},{3,2,0,1},{3,2,1,0}}; + +// 1D simplex noise +float snoise1(float x) { + + int i0 = FASTFLOOR(x); + int i1 = i0 + 1; + float x0 = x - i0; + float x1 = x0 - 1.0f; + + float n0, n1; + + float t0 = 1.0f - x0*x0; +// if(t0 < 0.0f) t0 = 0.0f; // this never happens for the 1D case + t0 *= t0; + n0 = t0 * t0 * grad1(perm[i0 & 0xff], x0); + + float t1 = 1.0f - x1*x1; +// if(t1 < 0.0f) t1 = 0.0f; // this never happens for the 1D case + t1 *= t1; + n1 = t1 * t1 * grad1(perm[i1 & 0xff], x1); + // The maximum value of this noise is 8*(3/4)^4 = 2.53125 + // A factor of 0.395 would scale to fit exactly within [-1,1], but + // we want to match PRMan's 1D noise, so we scale it down some more. + return 0.25f * (n0 + n1); + +} + +// 2D simplex noise +float snoise2(float x, float y) { + +#define F2 0.366025403 // F2 = 0.5*(sqrt(3.0)-1.0) +#define G2 0.211324865 // G2 = (3.0-Math.sqrt(3.0))/6.0 + + float n0, n1, n2; // Noise contributions from the three corners + + // Skew the input space to determine which simplex cell we're in + float s = (x+y)*F2; // Hairy factor for 2D + float xs = x + s; + float ys = y + s; + int i = FASTFLOOR(xs); + int j = FASTFLOOR(ys); + + float t = (float)(i+j)*G2; + float X0 = i-t; // Unskew the cell origin back to (x,y) space + float Y0 = j-t; + float x0 = x-X0; // The x,y distances from the cell origin + float y0 = y-Y0; + + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + + float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + float y1 = y0 - j1 + G2; + float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords + float y2 = y0 - 1.0f + 2.0f * G2; + + // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds + int ii = i & 0xff; + int jj = j & 0xff; + + // Calculate the contribution from the three corners + float t0 = 0.5f - x0*x0-y0*y0; + if(t0 < 0.0f) n0 = 0.0f; + else { + t0 *= t0; + n0 = t0 * t0 * grad2(perm[ii+perm[jj]], x0, y0); + } + + float t1 = 0.5f - x1*x1-y1*y1; + if(t1 < 0.0f) n1 = 0.0f; + else { + t1 *= t1; + n1 = t1 * t1 * grad2(perm[ii+i1+perm[jj+j1]], x1, y1); + } + + float t2 = 0.5f - x2*x2-y2*y2; + if(t2 < 0.0f) n2 = 0.0f; + else { + t2 *= t2; + n2 = t2 * t2 * grad2(perm[ii+1+perm[jj+1]], x2, y2); + } + + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! + } + +// 3D simplex noise +float snoise3(float x, float y, float z) { + +// Simple skewing factors for the 3D case +#define F3 0.333333333 +#define G3 0.166666667 + + float n0, n1, n2, n3; // Noise contributions from the four corners + + // Skew the input space to determine which simplex cell we're in + float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D + float xs = x+s; + float ys = y+s; + float zs = z+s; + int i = FASTFLOOR(xs); + int j = FASTFLOOR(ys); + int k = FASTFLOOR(zs); + + float t = (float)(i+j+k)*G3; + float X0 = i-t; // Unskew the cell origin back to (x,y,z) space + float Y0 = j-t; + float Z0 = k-t; + float x0 = x-X0; // The x,y,z distances from the cell origin + float y0 = y-Y0; + float z0 = z-Z0; + + // For the 3D case, the simplex shape is a slightly irregular tetrahedron. + // Determine which simplex we are in. + int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords + int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords + +/* This code would benefit from a backport from the GLSL version! */ + if(x0>=y0) { + if(y0>=z0) + { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order + else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order + else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order + } + else { // x0 y0) ? 32 : 0; + int c2 = (x0 > z0) ? 16 : 0; + int c3 = (y0 > z0) ? 8 : 0; + int c4 = (x0 > w0) ? 4 : 0; + int c5 = (y0 > w0) ? 2 : 0; + int c6 = (z0 > w0) ? 1 : 0; + int c = c1 + c2 + c3 + c4 + c5 + c6; + + int i1, j1, k1, l1; // The integer offsets for the second simplex corner + int i2, j2, k2, l2; // The integer offsets for the third simplex corner + int i3, j3, k3, l3; // The integer offsets for the fourth simplex corner + + // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order. + // Many values of c will never occur, since e.g. x>y>z>w makes x=3 ? 1 : 0; + j1 = simplex[c][1]>=3 ? 1 : 0; + k1 = simplex[c][2]>=3 ? 1 : 0; + l1 = simplex[c][3]>=3 ? 1 : 0; + // The number 2 in the "simplex" array is at the second largest coordinate. + i2 = simplex[c][0]>=2 ? 1 : 0; + j2 = simplex[c][1]>=2 ? 1 : 0; + k2 = simplex[c][2]>=2 ? 1 : 0; + l2 = simplex[c][3]>=2 ? 1 : 0; + // The number 1 in the "simplex" array is at the second smallest coordinate. + i3 = simplex[c][0]>=1 ? 1 : 0; + j3 = simplex[c][1]>=1 ? 1 : 0; + k3 = simplex[c][2]>=1 ? 1 : 0; + l3 = simplex[c][3]>=1 ? 1 : 0; + // The fifth corner has all coordinate offsets = 1, so no need to look that up. + + float x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords + float y1 = y0 - j1 + G4; + float z1 = z0 - k1 + G4; + float w1 = w0 - l1 + G4; + float x2 = x0 - i2 + 2.0f*G4; // Offsets for third corner in (x,y,z,w) coords + float y2 = y0 - j2 + 2.0f*G4; + float z2 = z0 - k2 + 2.0f*G4; + float w2 = w0 - l2 + 2.0f*G4; + float x3 = x0 - i3 + 3.0f*G4; // Offsets for fourth corner in (x,y,z,w) coords + float y3 = y0 - j3 + 3.0f*G4; + float z3 = z0 - k3 + 3.0f*G4; + float w3 = w0 - l3 + 3.0f*G4; + float x4 = x0 - 1.0f + 4.0f*G4; // Offsets for last corner in (x,y,z,w) coords + float y4 = y0 - 1.0f + 4.0f*G4; + float z4 = z0 - 1.0f + 4.0f*G4; + float w4 = w0 - 1.0f + 4.0f*G4; + + // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds + int ii = i & 0xff; + int jj = j & 0xff; + int kk = k & 0xff; + int ll = l & 0xff; + + // Calculate the contribution from the five corners + float t0 = 0.6f - x0*x0 - y0*y0 - z0*z0 - w0*w0; + if(t0 < 0.0f) n0 = 0.0f; + else { + t0 *= t0; + n0 = t0 * t0 * grad4(perm[ii+perm[jj+perm[kk+perm[ll]]]], x0, y0, z0, w0); + } + + float t1 = 0.6f - x1*x1 - y1*y1 - z1*z1 - w1*w1; + if(t1 < 0.0f) n1 = 0.0f; + else { + t1 *= t1; + n1 = t1 * t1 * grad4(perm[ii+i1+perm[jj+j1+perm[kk+k1+perm[ll+l1]]]], x1, y1, z1, w1); + } + + float t2 = 0.6f - x2*x2 - y2*y2 - z2*z2 - w2*w2; + if(t2 < 0.0f) n2 = 0.0f; + else { + t2 *= t2; + n2 = t2 * t2 * grad4(perm[ii+i2+perm[jj+j2+perm[kk+k2+perm[ll+l2]]]], x2, y2, z2, w2); + } + + float t3 = 0.6f - x3*x3 - y3*y3 - z3*z3 - w3*w3; + if(t3 < 0.0f) n3 = 0.0f; + else { + t3 *= t3; + n3 = t3 * t3 * grad4(perm[ii+i3+perm[jj+j3+perm[kk+k3+perm[ll+l3]]]], x3, y3, z3, w3); + } + + float t4 = 0.6f - x4*x4 - y4*y4 - z4*z4 - w4*w4; + if(t4 < 0.0f) n4 = 0.0f; + else { + t4 *= t4; + n4 = t4 * t4 * grad4(perm[ii+1+perm[jj+1+perm[kk+1+perm[ll+1]]]], x4, y4, z4, w4); + } + + // Sum up and scale the result to cover the range [-1,1] + return 27.0f * (n0 + n1 + n2 + n3 + n4); // TODO: The scale factor is preliminary! + } +//--------------------------------------------------------------------- diff --git a/Source/Asteroids/simplexnoise1234.h b/Source/Asteroids/simplexnoise1234.h new file mode 100644 index 0000000..6f0111e --- /dev/null +++ b/Source/Asteroids/simplexnoise1234.h @@ -0,0 +1,66 @@ +/* SimplexNoise1234, Simplex noise with true analytic + * derivative in 1D to 4D. + * + * Author: Stefan Gustavson, 2003-2005 + * Contact: stegu@itn.liu.se + */ + +/* +This code was GPL licensed until February 2011. +As the original author of this code, I hereby +release it irrevocably into the public domain. +Please feel free to use it for whatever you want. +Credit is appreciated where appropriate, and I also +appreciate being told where this code finds any use, +but you may do as you like. Alternatively, if you want +to have a familiar OSI-approved license, you may use +This code under the terms of the MIT license: +Copyright (C) 2003-2005 by Stefan Gustavson. All rights reserved. +This code is licensed to you under the terms of the MIT license: +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +/** \file + \brief Header for "simplexnoise1234.c" for producing Perlin simplex noise. + \author Stefan Gustavson (stegu@itn.liu.se) +*/ + +/* + * This is a clean, fast, modern and free Perlin Simplex noise function. + * It is a stand-alone compilation unit with no external dependencies, + * highly reusable without source code modifications. + * + * + * Note: + * Replacing the "float" type with "double" can actually make this run faster + * on some platforms. Having both versions could be useful. + */ + +/** 1D, 2D, 3D and 4D float Perlin simplex noise + */ +#ifdef __cplusplus +extern "C" { +#endif + + float snoise1( float x ); + float snoise2( float x, float y ); + float snoise3( float x, float y, float z ); + float snoise4( float x, float y, float z, float w ); + +#ifdef __cplusplus +} +#endif diff --git a/Source/Asteroids/simulation.cpp b/Source/Asteroids/simulation.cpp new file mode 100644 index 0000000..e69de29 diff --git a/Source/Asteroids/simulation.h b/Source/Asteroids/simulation.h new file mode 100644 index 0000000..e69de29