diff --git a/src/Silkworm.c b/src/Silkworm.c index 280bacb..77b4979 100644 --- a/src/Silkworm.c +++ b/src/Silkworm.c @@ -47,18 +47,19 @@ typedef struct Silkworm_Context Silkworm_Cloth** cloths; uint32_t clothCount; - - uint64_t* nodeIndicesToDestroy; - uint32_t nodeIndicesToDestroyCount; - - uint64_t* linkIndicesToDestroy; - uint32_t linkIndicesToDestroyCount; + uint32_t clothCapacity; uint64_t* nodeIndexStack; uint32_t nodeIndexStackCount; + uint32_t nodeIndexStackCapacity; uint64_t* linkIndexStack; uint32_t linkIndexStackCount; + uint32_t linkIndexStackCapacity; + + uint64_t* clothIndexStack; + uint32_t clothIndexStackCount; + uint32_t clothIndexStackCapacity; float gravity; float xBound; @@ -78,30 +79,167 @@ void Silkworm_Init() context->nodes = NULL; context->nodeCount = 0; + context->links = NULL; context->linkCount = 0; - context->nodeIndicesToDestroy = NULL; - context->nodeIndicesToDestroyCount = 0; - - context->linkIndicesToDestroy = NULL; - context->linkIndicesToDestroyCount = 0; - - context->nodeIndexStack = NULL; - context->nodeIndexStackCount = 0; - - context->linkIndexStack = NULL; - context->linkIndexStackCount = 0; - context->cloths = NULL; context->clothCount = 0; + context->nodeIndexStackCapacity = 16; + context->nodeIndexStack = malloc(sizeof(uint64_t) * context->nodeIndexStackCapacity); + context->nodeIndexStackCount = 0; + + context->linkIndexStackCapacity = 16; + context->linkIndexStack = malloc(sizeof(uint64_t) * context->linkIndexStackCapacity); + context->linkIndexStackCount = 0; + + context->clothIndexStackCapacity = 16; + context->clothIndexStack = malloc(sizeof(uint64_t) * context->clothIndexStackCapacity); + context->clothIndexStackCount = 0; + context->gravity = 200; context->xBound = 1000; context->yBound = 1000; context->clothDensity = 4; } +static inline Silkworm_Node* LookupNode(uint64_t nodeId) +{ + return context->nodes[nodeId]; +} + +static inline Silkworm_Link* LookupLink(uint64_t linkId) +{ + return context->links[linkId]; +} + +static inline Silkworm_Cloth* LookupCloth(uint64_t clothId) +{ + return context->cloths[clothId]; +} + +/* we defer actual destruction to Update to avoid issues with NULL */ + +void Silkworm_DestroyNode(double nodeId) +{ + uint32_t i; + Silkworm_Node* node = LookupNode((uint64_t)nodeId); + + if (node != NULL) + { + node->markedForDestroy = true; + + for (i = 0; i < node->linkCount; i += 1) + { + if (node->links[i] != NULL) + { + node->links[i]->markedForDestroy = true; + } + } + } +} + +void Silkworm_Internal_DestroyLink(Silkworm_Link* link) +{ + link->markedForDestroy = true; +} + +void Silkworm_DestroyLink(double linkId) +{ + Silkworm_Internal_DestroyLink(LookupLink((uint64_t)linkId)); +} + +void Silkworm_Internal_DestroyCloth(Silkworm_Cloth* cloth) +{ + uint32_t i, j; + + context->cloths[cloth->id] = NULL; + + for (i = 0; i < cloth->horizontalNodeCount; i += 1) + { + for (j = 0; j < cloth->verticalNodeCount; j += 1) + { + Silkworm_DestroyNode((double)cloth->nodeIndices[i][j]); + } + free(cloth->nodeIndices[i]); + } + free(cloth->nodeIndices); + free(cloth); + + if (context->clothIndexStackCount >= context->clothIndexStackCapacity) + { + context->clothIndexStackCapacity *= 2; + context->nodeIndexStack = realloc(context->nodeIndexStack, sizeof(uint64_t) * context->nodeIndexStackCapacity); + } + + context->clothIndexStack[context->clothIndexStackCount] = cloth->id; + context->clothIndexStackCount += 1; +} + +void Silkworm_DestroyCloth(double clothId) +{ + Silkworm_Internal_DestroyCloth(LookupCloth((uint64_t)clothId)); +} + +void Silkworm_PerformDestroys() +{ + uint32_t i, j; + + for (i = 0; i < context->linkCount; i += 1) + { + if (context->links[i] != NULL && context->links[i]->markedForDestroy) + { + for (j = 0; j < context->links[i]->a->linkCount; j += 1) + { + if (context->links[i]->a->links[j] == context->links[i]) + { + context->links[i]->a->links[j] = NULL; + } + } + + for (j = 0; j < context->links[i]->b->linkCount; j += 1) + { + if (context->links[i]->b->links[j] == context->links[i]) + { + context->links[i]->b->links[j] = NULL; + } + } + + free(context->links[i]); + context->links[i] = NULL; + + if (context->linkIndexStackCount >= context->linkIndexStackCapacity) + { + context->linkIndexStackCapacity *= 2; + context->linkIndexStack = realloc(context->linkIndexStack, sizeof(uint64_t) * context->linkIndexStackCapacity); + } + + context->linkIndexStack[context->linkIndexStackCount] = i; + context->linkIndexStackCount += 1; + } + } + + for (i = 0; i < context->nodeCount; i += 1) + { + if (context->nodes[i] != NULL && context->nodes[i]->markedForDestroy) + { + free(context->nodes[i]->links); + free(context->nodes[i]); + context->nodes[i] = NULL; + + if (context->nodeIndexStackCount >= context->nodeIndexStackCapacity) + { + context->nodeIndexStackCapacity *= 2; + context->nodeIndexStack = realloc(context->nodeIndexStack, sizeof(uint64_t) * context->nodeIndexStackCapacity); + } + + context->nodeIndexStack[context->nodeIndexStackCount] = i; + context->nodeIndexStackCount += 1; + } + } +} + void Silkworm_Update(double deltaTime) { uint32_t i, j; @@ -131,9 +269,7 @@ void Silkworm_Update(double deltaTime) if (distanceMoved > link->tearThreshold) { - context->linkIndicesToDestroy = realloc(context->linkIndicesToDestroy, sizeof(uint64_t) * (context->linkIndicesToDestroyCount + 1)); - context->linkIndicesToDestroy[context->linkIndicesToDestroyCount] = link->id; - context->linkIndicesToDestroyCount += 1; + Silkworm_DestroyLink((double)link->id); } else { @@ -151,7 +287,6 @@ void Silkworm_Update(double deltaTime) } } } - } for (j = 0; j < context->nodeCount; j += 1) @@ -179,50 +314,13 @@ void Silkworm_Update(double deltaTime) if (fabs(node->position.x) > context->xBound || fabs(node->position.x) > context->yBound) { - context->nodeIndicesToDestroy = realloc(context->nodeIndicesToDestroy, sizeof(uint64_t) * (context->nodeIndicesToDestroyCount + 1)); - context->nodeIndicesToDestroy[context->nodeIndicesToDestroyCount] = node->id; - context->nodeIndicesToDestroyCount += 1; + Silkworm_DestroyNode((double)node->id); } } } } - for (j = 0; j < context->nodeIndicesToDestroyCount; j += 1) - { - uint64_t nodeIndex = context->nodeIndicesToDestroy[j]; - - free(context->nodes[nodeIndex]); - context->nodes[nodeIndex] = NULL; - - context->nodeIndexStack = realloc(context->nodeIndexStack, sizeof(uint64_t) * (context->nodeIndexStackCount + 1)); - context->nodeIndexStack[context->nodeIndexStackCount] = nodeIndex; - context->nodeIndexStackCount += 1; - } - - for (j = 0; j < context->linkIndicesToDestroyCount; j += 1) - { - uint64_t linkIndex = context->linkIndicesToDestroy[j]; - - free(context->links[linkIndex]); - context->links[linkIndex] = NULL; - - context->linkIndexStack = realloc(context->linkIndexStack, sizeof(uint64_t) * (context->linkIndexStackCount + 1)); - context->linkIndexStack[context->linkIndexStackCount] = linkIndex; - context->linkIndexStackCount += 1; - } - - context->nodeIndicesToDestroyCount = 0; - context->linkIndicesToDestroyCount = 0; -} - -static inline Silkworm_Node* LookupNode(double nodeId) -{ - return context->nodes[(uint64_t)nodeId]; -} - -static inline Silkworm_Cloth* LookupCloth(double clothId) -{ - return context->cloths[(uint64_t)clothId]; + Silkworm_PerformDestroys(); } double Silkworm_CreateNode(double xPosition, double yPosition, double mass, double friction, double radius, double pushFactor) @@ -233,20 +331,18 @@ double Silkworm_CreateNode(double xPosition, double yPosition, double mass, doub if (context->nodeIndexStackCount > 0) { id = context->nodeIndexStack[context->nodeIndexStackCount - 1]; - context->nodeIndexStack = realloc(context->nodeIndexStack, sizeof(uint64_t) * (context->nodeIndexStackCount - 1)); context->nodeIndexStackCount -= 1; - - context->nodes[id] = node; } else { id = context->nodeCount; context->nodes = realloc(context->nodes, sizeof(Silkworm_Node*) * (context->nodeCount + 1)); - context->nodes[context->nodeCount] = node; context->nodeCount += 1; } + context->nodes[id] = node; + node->id = id; node->position.x = (float)xPosition; node->position.y = (float)yPosition; @@ -262,6 +358,9 @@ double Silkworm_CreateNode(double xPosition, double yPosition, double mass, doub node->pushFactor = (float)pushFactor; node->pinned = false; node->destroyable = false; + node->markedForDestroy = false; + node->links = NULL; + node->linkCount = 0; return (double)node->id; } @@ -295,6 +394,29 @@ void Silkworm_ClothNodeUnpin(double clothId, double i, double j) context->nodes[cloth->nodeIndices[(uint64_t)i][(uint64_t)j]]->pinned = false; } +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) { Silkworm_Node *nodeA = LookupNode(aId); @@ -307,25 +429,32 @@ double Silkworm_CreateLink(double aId, double bId, double distance, double tearT if (context->linkIndexStackCount > 0) { id = context->linkIndexStack[context->linkIndexStackCount - 1]; - context->linkIndexStack = realloc(context->linkIndexStack, sizeof(uint64_t) * (context->linkIndexStackCount - 1)); context->linkIndexStackCount -= 1; - - context->links[id] = link; } else { id = context->linkCount; context->links = realloc(context->links, sizeof(Silkworm_Link*) * (context->linkCount + 1)); - context->links[context->linkCount] = link; context->linkCount += 1; } + context->links[id] = link; + link->id = id; link->a = nodeA; link->b = nodeB; link->distance = (float)distance; link->tearThreshold = (float)tearThreshold; + link->markedForDestroy = false; + + link->a->links = realloc(link->a->links, sizeof(Silkworm_Link*) * (link->a->linkCount + 1)); + link->a->links[link->a->linkCount] = link; + link->a->linkCount += 1; + + link->b->links = realloc(link->b->links, sizeof(Silkworm_Link*) * (link->b->linkCount + 1)); + link->b->links[link->b->linkCount] = link; + link->b->linkCount += 1; return (double)link->id; } @@ -341,10 +470,25 @@ double Silkworm_CreateCloth(double xPosition, double yPosition, double horizonta cloth->verticalNodeCount = (uint32_t) verticalNodeCount; cloth->nodeIndices = malloc(sizeof(uint64_t*) * cloth->horizontalNodeCount); - cloth->id = context->clothCount; - context->cloths = realloc(context->cloths, sizeof(Silkworm_Cloth*) * (context->clothCount + 1)); - context->cloths[context->clothCount] = cloth; - context->clothCount += 1; + uint64_t id; + + if (context->clothIndexStackCount > 0) + { + id = context->clothIndexStack[context->clothIndexStackCount - 1]; + context->clothIndexStackCount -= 1; + + context->cloths[id] = cloth; + } + else + { + id = context->clothCount; + + context->cloths = realloc(context->cloths, sizeof(Silkworm_Cloth*) * (context->clothCount + 1)); + context->cloths[id] = cloth; + context->clothCount += 1; + } + + cloth->id = id; for (i = 0; i < horizontalNodeCount; i += 1) { @@ -437,6 +581,35 @@ double Silkworm_CreateCloth(double xPosition, double yPosition, double horizonta return (double)cloth->id; } +void Silkworm_ApplyWind(double xSpeed, double ySpeed) +{ + uint32_t i, j, k; + Silkworm_Cloth* cloth; + Silkworm_Node* node; + + for (i = 0; i < context->clothCount; i += 1) + { + if (context->cloths[i] != NULL) + { + cloth = context->cloths[i]; + + for (j = 0; j < cloth->horizontalNodeCount; j += 1) + { + for (k = 0; k < cloth->verticalNodeCount; k += 1) + { + node = context->nodes[cloth->nodeIndices[j][k]]; + + if (node != NULL && !node->pinned) + { + node->position.x += (float)xSpeed * 0.05f * cloth->windFactor; + node->position.y += (float)ySpeed * 0.05f * cloth->windFactor; + } + } + } + } + } +} + void Silkworm_SetTriangleBuffer(const char* bufferId) { context->currentBufferAddress = (uint8_t*)bufferId; @@ -557,3 +730,18 @@ double Silkworm_ClothFillTriangleBuffer(double clothId, double leftUV, double wi return (double)triangleCount; } + +void Silkworm_ClearAll() +{ + uint32_t i; + + for (i = 0; i < context->clothCount; i += 1) + { + if (context->cloths[i] != NULL) + { + Silkworm_Internal_DestroyCloth(context->cloths[i]); + } + } + + Silkworm_PerformDestroys(); +} diff --git a/src/Silkworm.h b/src/Silkworm.h index 64b285f..440e1ff 100644 --- a/src/Silkworm.h +++ b/src/Silkworm.h @@ -49,6 +49,8 @@ typedef struct Silkworm_Vector2 float y; } Silkworm_Vector2; +typedef struct Silkworm_Link Silkworm_Link; + typedef struct Silkworm_Node { uint64_t id; @@ -62,16 +64,23 @@ typedef struct Silkworm_Node 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; -typedef struct Silkworm_Link +struct Silkworm_Link { uint64_t id; Silkworm_Node *a; Silkworm_Node *b; float distance; float tearThreshold; -} Silkworm_Link; + + bool markedForDestroy; /* mutual recursion on nodes/links so this makes it easier to track destroys */ +}; typedef enum Silkworm_ClothTriangleOrientation { @@ -133,18 +142,25 @@ typedef struct Silkworm_Rope SILKWORMAPI void Silkworm_Init(); SILKWORMAPI void Silkworm_Update(double delta); +SILKWORMAPI void Silkworm_ApplyWind(double xSpeed, double ySpeed); SILKWORMAPI double Silkworm_CreateNode(double xPosition, double yPosition, double mass, double friction, double radius, double pushFactor); SILKWORMAPI void Silkworm_NodeSetDestroyable(double nodeId); -SILKWORMAPI void Silkworm_ClothNodePin(double clothId, double i, double j); -SILKWORMAPI void Silkworm_ClothNodeUnpin(double clothId, double i, double j); + 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); +SILKWORMAPI void Silkworm_ClothNodePin(double clothId, double i, double j); +SILKWORMAPI void Silkworm_ClothNodeUnpin(double clothId, double i, double j); +SILKWORMAPI void Silkworm_ClothNodeDestroy(double clothId, double i, double j); +SILKWORMAPI void Silkworm_DestroyCloth(double clothId); 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_ClearAll(); + #endif /* SILKWORM_H */