From 2fc8d0a7816fadd6f77cd73674b667b1dee7fdc6 Mon Sep 17 00:00:00 2001 From: cosmonaut Date: Fri, 24 Sep 2021 13:48:35 -0700 Subject: [PATCH] node to triangle lookups --- src/Silkworm.c | 259 +++++++++++++++++++++++++++++++++++++++++++++---- src/Silkworm.h | 88 +---------------- 2 files changed, 245 insertions(+), 102 deletions(-) diff --git a/src/Silkworm.c b/src/Silkworm.c index 77b4979..82c7056 100644 --- a/src/Silkworm.c +++ b/src/Silkworm.c @@ -29,6 +29,174 @@ #include #include +typedef struct Silkworm_Vector2 +{ + float x; + float y; +} Silkworm_Vector2; + +typedef struct Silkworm_Link Silkworm_Link; + +typedef struct Silkworm_Node +{ + uint64_t id; + Silkworm_Vector2 position; + Silkworm_Vector2 previousPosition; + Silkworm_Vector2 velocity; + Silkworm_Vector2 acceleration; + float mass; + float friction; + float radius; + float pinned; + float pushFactor; + bool destroyable; + + bool markedForDestroy; /* mutual recursion on nodes/links so this makes it easier to track destroys */ + + Silkworm_Link** links; + uint32_t linkCount; +} Silkworm_Node; + +struct Silkworm_Link +{ + uint64_t id; + Silkworm_Node* a; + Silkworm_Node* b; + float distance; + float tearThreshold; + + bool markedForDestroy; /* mutual recursion on nodes/links so this makes it easier to track destroys */ +}; + +typedef enum Silkworm_ClothTriangleOrientation +{ + UpperLeft, + BottomRight +} Silkworm_ClothTriangleOrientation; + +typedef struct Silkworm_Triangle +{ + uint64_t id; + Silkworm_Node* a; + Silkworm_Node* b; + Silkworm_Node* c; + + uint32_t aHorizontalIndex; + uint32_t bHorizontalIndex; + uint32_t cHorizontalIndex; + + uint32_t aVerticalIndex; + uint32_t bVerticalIndex; + uint32_t cVerticalIndex; + + Silkworm_ClothTriangleOrientation orientation; +} Silkworm_Triangle; + +typedef struct NodeTriangleHashMap +{ + Silkworm_Node* key; + uint32_t *indexArray; + uint32_t indexArrayCount; +} NodeTriangleHashMap; + +typedef struct NodeTriangleHashArray +{ + NodeTriangleHashMap* elements; + uint32_t count; +} NodeTriangleHashArray; + +#define NUM_NODE_TRIANGLE_HASH_BUCKETS 1031 + +typedef struct NodeTriangleHashTable +{ + NodeTriangleHashArray buckets[NUM_NODE_TRIANGLE_HASH_BUCKETS]; +} NodeTriangleHashTable; + +static inline uint64_t NodeTriangleHashTable_GetHashCode(Silkworm_Node *key) +{ + return 97 + (uint64_t)(size_t)key; +} + +static inline uint32_t* NodeTriangleHashTable_Fetch( + NodeTriangleHashTable *table, + Silkworm_Node *key, + uint32_t *arrayCount +) { + uint32_t i; + uint64_t hashcode = NodeTriangleHashTable_GetHashCode(key); + NodeTriangleHashArray* arr = &table->buckets[hashcode % NUM_NODE_TRIANGLE_HASH_BUCKETS]; + + for (i = 0; i < arr->count; i += 1) + { + if (arr->elements[i].key == key) + { + *arrayCount = arr->elements[i].indexArrayCount; + return arr->elements[i].indexArray; + } + } + + *arrayCount = 0; + return NULL; +} + +static inline void NodeTriangleHashTable_Insert( + NodeTriangleHashTable* table, + Silkworm_Node* key, + uint32_t index +) { + uint32_t i; + uint64_t hashcode = NodeTriangleHashTable_GetHashCode(key); + NodeTriangleHashArray* arr = &table->buckets[hashcode % NUM_NODE_TRIANGLE_HASH_BUCKETS]; + bool foundKey = false; + + for (i = 0; i < arr->count; i += 1) + { + if (arr->elements[i].key == key) + { + arr->elements[i].indexArray = realloc(arr->elements[i].indexArray, sizeof(uint32_t) * (arr->elements[i].indexArrayCount + 1)); + arr->elements[i].indexArray[arr->elements[i].indexArrayCount] = index; + arr->elements[i].indexArrayCount += 1; + foundKey = true; + break; + } + } + + if (!foundKey) + { + arr->elements = realloc(arr->elements, sizeof(NodeTriangleHashMap) * (arr->count + 1)); + + arr->elements[arr->count].key = key; + arr->elements[arr->count].indexArray = malloc(sizeof(uint32_t)); + arr->elements[arr->count].indexArray[0] = index; + arr->elements[arr->count].indexArrayCount = 1; + + arr->count += 1; + } +} + +typedef struct Silkworm_Cloth +{ + uint64_t id; + float windFactor; + + uint32_t horizontalNodeCount; + uint32_t verticalNodeCount; + + uint64_t** nodeIndices; /* x by y grid of indices */ + + Silkworm_Triangle** triangles; /* array of pointers so we can use NULL */ + uint32_t triangleCount; + + NodeTriangleHashTable nodeHash; +} Silkworm_Cloth; + +typedef struct Silkworm_Rope +{ + uint32_t nodeCount; + + uint32_t* nodeIndices; +} Silkworm_Rope; + typedef struct Silkworm_Color { uint8_t r; @@ -156,6 +324,15 @@ void Silkworm_Internal_DestroyCloth(Silkworm_Cloth* cloth) context->cloths[cloth->id] = NULL; + for (i = 0; i < NUM_NODE_TRIANGLE_HASH_BUCKETS; i += 1) + { + for (j = 0; j < cloth->nodeHash.buckets[i].count; j += 1) + { + free(cloth->nodeHash.buckets[i].elements[j].indexArray); + } + free(cloth->nodeHash.buckets[i].elements); + } + for (i = 0; i < cloth->horizontalNodeCount; i += 1) { for (j = 0; j < cloth->verticalNodeCount; j += 1) @@ -184,7 +361,7 @@ void Silkworm_DestroyCloth(double clothId) void Silkworm_PerformDestroys() { - uint32_t i, j; + uint32_t i, j, k; for (i = 0; i < context->linkCount; i += 1) { @@ -222,8 +399,32 @@ void Silkworm_PerformDestroys() for (i = 0; i < context->nodeCount; i += 1) { - if (context->nodes[i] != NULL && context->nodes[i]->markedForDestroy) + Silkworm_Node* node = context->nodes[i]; + if (node != NULL && node->markedForDestroy) { + /* find cloth to remove from relevant triangles */ + for (j = 0; j < context->clothCount; j += 1) + { + Silkworm_Cloth* cloth = context->cloths[j]; + + if (cloth != NULL) + { + uint32_t triangleIndexCount = 0; + uint32_t* triangleIndices = NodeTriangleHashTable_Fetch(&cloth->nodeHash, node, &triangleIndexCount); + + for (k = 0; k < triangleIndexCount; k += 1) + { + uint32_t triangleIndex = triangleIndices[k]; + + if (cloth->triangles[triangleIndex] != NULL) + { + free(cloth->triangles[triangleIndex]); + cloth->triangles[triangleIndex] = NULL; + } + } + } + } + free(context->nodes[i]->links); free(context->nodes[i]); context->nodes[i] = NULL; @@ -396,25 +597,10 @@ void Silkworm_ClothNodeUnpin(double clothId, double i, double j) void Silkworm_ClothNodeDestroy(double clothId, double i, double j) { - uint32_t iterator; - Silkworm_Cloth* cloth = LookupCloth(clothId); uint64_t nodeIndex = cloth->nodeIndices[(uint64_t)i][(uint64_t)j]; Silkworm_Node* node = context->nodes[nodeIndex]; Silkworm_DestroyNode(nodeIndex); - - /* fixme: this should be hashed */ - for (iterator = 0; iterator < cloth->triangleCount; iterator += 1) - { - if (cloth->triangles[iterator] != NULL) - { - if (cloth->triangles[iterator]->a == node || cloth->triangles[iterator]->b == node || cloth->triangles[iterator]->c == node) - { - free(cloth->triangles[iterator]); - cloth->triangles[iterator] = NULL; - } - } - } } double Silkworm_CreateLink(double aId, double bId, double distance, double tearThreshold) @@ -490,6 +676,12 @@ double Silkworm_CreateCloth(double xPosition, double yPosition, double horizonta cloth->id = id; + for (i = 0; i < NUM_NODE_TRIANGLE_HASH_BUCKETS; i += 1) + { + cloth->nodeHash.buckets[i].elements = NULL; + cloth->nodeHash.buckets[i].count = 0; + } + for (i = 0; i < horizontalNodeCount; i += 1) { cloth->nodeIndices[i] = malloc(sizeof(uint64_t) * cloth->verticalNodeCount); @@ -534,6 +726,10 @@ double Silkworm_CreateCloth(double xPosition, double yPosition, double horizonta cloth->triangles[triangleIndex]->cHorizontalIndex = i; cloth->triangles[triangleIndex]->cVerticalIndex = j + 1; + NodeTriangleHashTable_Insert(&cloth->nodeHash, context->nodes[cloth->nodeIndices[i][j]], triangleIndex); + NodeTriangleHashTable_Insert(&cloth->nodeHash, context->nodes[cloth->nodeIndices[i + 1][j]], triangleIndex); + NodeTriangleHashTable_Insert(&cloth->nodeHash, context->nodes[cloth->nodeIndices[i][j + 1]], triangleIndex); + triangleIndex += 1; } @@ -555,6 +751,10 @@ double Silkworm_CreateCloth(double xPosition, double yPosition, double horizonta cloth->triangles[triangleIndex]->cHorizontalIndex = i; cloth->triangles[triangleIndex]->cVerticalIndex = j - 1; + NodeTriangleHashTable_Insert(&cloth->nodeHash, context->nodes[cloth->nodeIndices[i][j]], triangleIndex); + NodeTriangleHashTable_Insert(&cloth->nodeHash, context->nodes[cloth->nodeIndices[i - 1][j]], triangleIndex); + NodeTriangleHashTable_Insert(&cloth->nodeHash, context->nodes[cloth->nodeIndices[i][j - 1]], triangleIndex); + triangleIndex += 1; } } @@ -731,6 +931,31 @@ double Silkworm_ClothFillTriangleBuffer(double clothId, double leftUV, double wi return (double)triangleCount; } +void Silkworm_DestroyNodesInRadius(double x, double y, double radius) +{ + /* TODO: spatial hash implementation */ + + uint32_t i; + + for (i = 0; i < context->nodeCount; i += 1) + { + Silkworm_Node* node = context->nodes[i]; + + if (node != NULL) + { + float xDistance = (float)fabs((float)x - node->position.x); + float yDistance = (float)fabs((float)y - node->position.y); + + float squareDistance = xDistance * xDistance + yDistance * yDistance; + + if (squareDistance <= radius * radius) + { + Silkworm_DestroyNode(i); + } + } + } +} + void Silkworm_ClearAll() { uint32_t i; diff --git a/src/Silkworm.h b/src/Silkworm.h index 440e1ff..9ba3b8e 100644 --- a/src/Silkworm.h +++ b/src/Silkworm.h @@ -43,90 +43,6 @@ extern "C" { #endif /* __cplusplus */ -typedef struct Silkworm_Vector2 -{ - float x; - float y; -} Silkworm_Vector2; - -typedef struct Silkworm_Link Silkworm_Link; - -typedef struct Silkworm_Node -{ - uint64_t id; - Silkworm_Vector2 position; - Silkworm_Vector2 previousPosition; - Silkworm_Vector2 velocity; - Silkworm_Vector2 acceleration; - float mass; - float friction; - float radius; - float pinned; - float pushFactor; - bool destroyable; - - bool markedForDestroy; /* mutual recursion on nodes/links so this makes it easier to track destroys */ - - Silkworm_Link** links; - uint32_t linkCount; -} Silkworm_Node; - -struct Silkworm_Link -{ - uint64_t id; - Silkworm_Node *a; - Silkworm_Node *b; - float distance; - float tearThreshold; - - bool markedForDestroy; /* mutual recursion on nodes/links so this makes it easier to track destroys */ -}; - -typedef enum Silkworm_ClothTriangleOrientation -{ - UpperLeft, - BottomRight -} Silkworm_ClothTriangleOrientation; - -typedef struct Silkworm_Triangle -{ - uint64_t id; - Silkworm_Node* a; - Silkworm_Node* b; - Silkworm_Node* c; - - uint32_t aHorizontalIndex; - uint32_t bHorizontalIndex; - uint32_t cHorizontalIndex; - - uint32_t aVerticalIndex; - uint32_t bVerticalIndex; - uint32_t cVerticalIndex; - - Silkworm_ClothTriangleOrientation orientation; -} Silkworm_Triangle; - -typedef struct Silkworm_Cloth -{ - uint64_t id; - float windFactor; - - uint32_t horizontalNodeCount; - uint32_t verticalNodeCount; - - uint64_t **nodeIndices; /* x by y grid of indices */ - - Silkworm_Triangle **triangles; /* array of pointers so we can use NULL */ - uint32_t triangleCount; -} Silkworm_Cloth; - -typedef struct Silkworm_Rope -{ - uint32_t nodeCount; - - uint32_t *nodeIndices; -} Silkworm_Rope; - /* Version API */ #define SILKWORM_ABI_VERSION 0 @@ -148,7 +64,6 @@ SILKWORMAPI double Silkworm_CreateNode(double xPosition, double yPosition, doubl SILKWORMAPI void Silkworm_NodeSetDestroyable(double nodeId); - SILKWORMAPI double Silkworm_CreateLink(double aId, double bId, double distance, double tearThreshold); SILKWORMAPI double Silkworm_CreateCloth(double xPosition, double yPosition, double horizontalNodeCount, double verticalNodeCount, double mass, double friction, double windFactor, double tearThreshold); @@ -161,6 +76,9 @@ SILKWORMAPI void Silkworm_SetTriangleBuffer(const char* bufferId); SILKWORMAPI double Silkworm_ClothFillTriangleBuffer(double clothId, double leftUV, double widthUV, double topUV, double heightUV); SILKWORMAPI void Silkworm_DestroyNode(double nodeId); + +SILKWORMAPI void Silkworm_DestroyNodesInRadius(double x, double y, double radius); + SILKWORMAPI void Silkworm_ClearAll(); #endif /* SILKWORM_H */