/* Silkworm - Verlet cloth physics in C * * Copyright (c) 2021 Evan Hemsley * * This software is provided 'as-is', without any express or implied warranty. * In no event will the authors be held liable for any damages arising from * the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software in a * product, an acknowledgment in the product documentation would be * appreciated but is not required. * * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * * 3. This notice may not be removed or altered from any source distribution. * * Evan "cosmonaut" Hemsley * */ #include "Silkworm.h" #include #include #define PI 3.14159265358979323846 typedef struct Silkworm_Vector2 { float x; float y; } Silkworm_Vector2; static inline Silkworm_Vector2 Vector2_Rotate(Silkworm_Vector2 vector, float angle) { Silkworm_Vector2 rotated; rotated.x = vector.x * cosf(angle) - vector.y * sinf(angle); rotated.y = vector.x * sinf(angle) + vector.y * cosf(angle); return rotated; } static inline Silkworm_Vector2 Vector2_Normalize(Silkworm_Vector2 vector) { float length = sqrtf(vector.x * vector.x + vector.y * vector.y); Silkworm_Vector2 normalized; normalized.x = vector.x / length; normalized.y = vector.y / length; return normalized; } typedef struct Silkworm_Link Silkworm_Link; typedef struct Silkworm_NodeClothReference { uint32_t clothId; uint32_t horizontalIndex; uint32_t verticalIndex; } Silkworm_NodeClothReference; typedef struct Silkworm_NodeRopeReference { uint32_t ropeId; uint32_t index; } Silkworm_NodeRopeReference; typedef enum Silkworm_NodeParentType { ClothParent, RopeParent } Silkworm_NodeParentType; 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; bool pinned; float pushFactor; float windFactor; bool destroyable; bool markedForDestroy; /* mutual recursion on nodes/links so this makes it easier to track destroys */ Silkworm_Link** links; uint32_t linkCount; Silkworm_NodeParentType nodeParentType; union { Silkworm_NodeClothReference clothReference; Silkworm_NodeRopeReference ropeReference; } parent; } 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 LinkTriangleHashMap { Silkworm_Link* key; uint32_t* indexArray; uint32_t indexArrayCount; } LinkTriangleHashMap; typedef struct LinkTriangleHashArray { LinkTriangleHashMap* elements; uint32_t count; } LinkTriangleHashArray; #define NUM_LINK_TRIANGLE_HASH_BUCKETS 1031 typedef struct LinkTriangleHashTable { LinkTriangleHashArray buckets[NUM_LINK_TRIANGLE_HASH_BUCKETS]; } LinkTriangleHashTable; static inline uint64_t LinkTriangleHashTable_GetHashCode(Silkworm_Link* key) { return 97 + (uint64_t)(size_t)key; } static inline uint32_t* LinkTriangleHashTable_Fetch( LinkTriangleHashTable* table, Silkworm_Link* key, uint32_t* arrayCount ) { uint32_t i; uint64_t hashcode = LinkTriangleHashTable_GetHashCode(key); LinkTriangleHashArray* arr = &table->buckets[hashcode % NUM_LINK_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 LinkTriangleHashTable_Insert( LinkTriangleHashTable* table, Silkworm_Link* key, uint32_t index ) { uint32_t i; uint64_t hashcode = LinkTriangleHashTable_GetHashCode(key); LinkTriangleHashArray* arr = &table->buckets[hashcode % NUM_LINK_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(LinkTriangleHashMap) * (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; uint32_t horizontalNodeCount; uint32_t verticalNodeCount; /* note that the cloth doesn't own the nodes, just has pointers to them */ Silkworm_Node*** nodes; /* x by y grid of indices */ Silkworm_Triangle** triangles; /* array of pointers so we can use NULL */ uint32_t triangleCount; NodeTriangleHashTable nodeHash; LinkTriangleHashTable linkHash; } Silkworm_Cloth; typedef struct Silkworm_Rope { uint64_t id; /* note that the rope doesn't own the nodes, just has pointers to them */ Silkworm_Node** nodes; uint32_t nodeCount; float windFactor; float nodeMass; float tearThreshold; float friction; float pushFactor; } Silkworm_Rope; typedef struct Silkworm_Color { uint8_t r; uint8_t g; uint8_t b; uint8_t a; } Silkworm_Color; typedef struct Silkworm_Context { Silkworm_Node** nodes; uint32_t nodeCount; Silkworm_Link** links; uint32_t linkCount; Silkworm_Cloth** cloths; uint32_t clothCount; Silkworm_Rope** ropes; uint32_t ropeCount; 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; uint64_t* ropeIndexStack; uint32_t ropeIndexStackCount; uint32_t ropeIndexStackCapacity; float gravity; float xBound; float yBound; uint32_t clothDensity; float timeElapsed; uint8_t* currentBufferAddress; /* GM doesnt let you pass more than 4 arguments with different types lol */ /* keep track of these so can do callbacks */ Silkworm_Vector2* nodeDestructionData; uint32_t nodeDestructionDataCount; uint32_t nodeDestructionDataCapacity; Silkworm_Vector2* linkDestructionData; uint32_t linkDestructionDataCount; uint32_t linkDestructionDataCapacity; } Silkworm_Context; static Silkworm_Context *context = NULL; #define CONSTRAINT_ITERATION_COUNT 3 /* TODO: make this a parameter? */ void Silkworm_Init() { context = malloc(sizeof(Silkworm_Context)); context->nodes = NULL; context->nodeCount = 0; context->links = NULL; context->linkCount = 0; context->cloths = NULL; context->clothCount = 0; context->ropes = NULL; context->ropeCount = 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->ropeIndexStackCapacity = 16; context->ropeIndexStack = malloc(sizeof(uint64_t) * context->ropeIndexStackCapacity); context->ropeIndexStackCount = 0; context->gravity = 200; context->xBound = 1000; context->yBound = 1000; context->clothDensity = 4; context->timeElapsed = 0; context->nodeDestructionDataCapacity = 16; context->nodeDestructionData = malloc(sizeof(Silkworm_Vector2) * context->nodeDestructionDataCapacity); context->nodeDestructionDataCount = 0; context->linkDestructionDataCapacity = 16; context->linkDestructionData = malloc(sizeof(Silkworm_Vector2) * context->nodeDestructionDataCapacity); context->linkDestructionDataCount = 0; } 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]; } static inline Silkworm_Rope* LookupRope(double ropeId) { return context->ropes[(uint32_t)ropeId]; } /* 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; if (context->linkDestructionDataCount >= context->linkDestructionDataCapacity) { context->linkDestructionDataCapacity *= 2; context->linkDestructionData = realloc(context->linkDestructionData, sizeof(Silkworm_Vector2) * context->linkDestructionDataCapacity); } Silkworm_Vector2 position; position.x = (link->a->position.x + link->b->position.x) / 2; position.y = (link->a->position.y + link->b->position.y) / 2; context->linkDestructionData[context->linkDestructionDataCount] = position; context->linkDestructionDataCount += 1; } void Silkworm_DestroyLink(double linkId) { Silkworm_Internal_DestroyLink(LookupLink((uint64_t)linkId)); } void Silkworm_Internal_DestroyCloth(Silkworm_Cloth* cloth) { uint32_t i, j; if (cloth != NULL) { 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); } if (cloth->nodeHash.buckets[i].elements != NULL) { free(cloth->nodeHash.buckets[i].elements); } } for (i = 0; i < NUM_LINK_TRIANGLE_HASH_BUCKETS; i += 1) { for (j = 0; j < cloth->linkHash.buckets[i].count; j += 1) { free(cloth->linkHash.buckets[i].elements[j].indexArray); } if (cloth->linkHash.buckets[i].elements != NULL) { free(cloth->linkHash.buckets[i].elements); } } for (i = 0; i < cloth->horizontalNodeCount; i += 1) { for (j = 0; j < cloth->verticalNodeCount; j += 1) { Silkworm_Node* node = cloth->nodes[i][j]; if (node != NULL) { Silkworm_DestroyNode((double)node->id); } } free(cloth->nodes[i]); } free(cloth->nodes); for (i = 0; i < cloth->triangleCount; i += 1) { if (cloth->triangles[i] != NULL) { free(cloth->triangles[i]); } } free(cloth->triangles); 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; free(cloth); } } void Silkworm_DestroyCloth(double clothId) { Silkworm_Internal_DestroyCloth(LookupCloth((uint64_t)clothId)); } void Silkworm_PerformDestroys() { uint32_t i, j, k; for (i = 0; i < context->linkCount; i += 1) { Silkworm_Link* link = context->links[i]; if (link != NULL && context->links[i]->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 = LinkTriangleHashTable_Fetch(&cloth->linkHash, link, &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; } } } } for (j = 0; j < link->a->linkCount; j += 1) { if (link->a->links[j] == context->links[i]) { link->a->links[j] = NULL; } } for (j = 0; j < link->b->linkCount; j += 1) { if (link->b->links[j] == context->links[i]) { link->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) { Silkworm_Node* node = context->nodes[i]; if (node != NULL && node->markedForDestroy) { if (node->nodeParentType == ClothParent) { Silkworm_Cloth* cloth = LookupCloth(node->parent.clothReference.clothId); 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; } } cloth->nodes[node->parent.clothReference.horizontalIndex][node->parent.clothReference.verticalIndex] = NULL; } } else if (node->nodeParentType == RopeParent) { Silkworm_Rope* rope = LookupRope((double)node->parent.ropeReference.ropeId); if (rope != NULL) { rope->nodes[node->parent.ropeReference.index] = NULL; } } 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; Silkworm_Link *link; Silkworm_Node *node; context->nodeDestructionDataCount = 0; context->linkDestructionDataCount = 0; float delta = (float)deltaTime; for (i = 0; i < CONSTRAINT_ITERATION_COUNT; i += 1) { for (j = 0; j < context->linkCount; j += 1) { link = context->links[j]; if (link != NULL) { float diffX = link->a->position.x - link->b->position.x; float diffY = link->a->position.y - link->b->position.y; float d = (float)sqrt(diffX * diffX + diffY * diffY); float difference = (link->distance - d) / d; float translateX = diffX * 0.5f * difference; float translateY = diffY * 0.5f * difference; float distanceMoved = (float)sqrt(translateX * translateX + translateY * translateY); if (distanceMoved > link->tearThreshold) { Silkworm_DestroyLink((double)link->id); } else { if (!link->a->pinned) { link->a->position.x += translateX; link->a->position.y += translateY; } if (!link->b->pinned) { link->b->position.x -= translateX; link->b->position.y -= translateY; } } } } } for (j = 0; j < context->nodeCount; j += 1) { node = context->nodes[j]; if (node != NULL) { if (!node->pinned) { float velocityX = (node->position.x - node->previousPosition.x) * node->friction; float velocityY = (node->position.y - node->previousPosition.y) * node->friction; float accelerationX = node->acceleration.x * delta * delta; float accelerationY = (node->acceleration.y + node->mass * context->gravity) * delta * delta; node->previousPosition.x = node->position.x; node->previousPosition.y = node->position.y; node->position.x += velocityX + accelerationX; node->position.y += velocityY + accelerationY; node->velocity.x = velocityX * delta; node->velocity.y = velocityY * delta; if (fabs(node->position.x) > context->xBound || fabs(node->position.x) > context->yBound) { Silkworm_DestroyNode((double)node->id); } } } } Silkworm_PerformDestroys(); context->timeElapsed += delta; } double Silkworm_CreateNode(double xPosition, double yPosition, double mass, double friction, double radius, double pushFactor, double windFactor) { Silkworm_Node* node = malloc(sizeof(Silkworm_Node)); uint64_t id; if (context->nodeIndexStackCount > 0) { id = context->nodeIndexStack[context->nodeIndexStackCount - 1]; context->nodeIndexStackCount -= 1; } else { id = context->nodeCount; context->nodes = realloc(context->nodes, sizeof(Silkworm_Node*) * (context->nodeCount + 1)); context->nodeCount += 1; } context->nodes[id] = node; node->id = id; node->position.x = (float)xPosition; node->position.y = (float)yPosition; node->previousPosition.x = (float)xPosition; node->previousPosition.y = (float)yPosition; node->velocity.x = 0; node->velocity.y = 0; node->acceleration.x = 0; node->acceleration.y = 0; node->mass = (float)mass; node->friction = (float)friction; node->radius = (float)radius; node->pushFactor = (float)pushFactor; node->windFactor = (float)windFactor; node->pinned = false; node->destroyable = false; node->markedForDestroy = false; node->links = NULL; node->linkCount = 0; return (double)node->id; } void Silkworm_NodeSetVelocity(double nodeId, double xVelocity, double yVelocity) { LookupNode(nodeId)->velocity.x = (float)xVelocity; LookupNode(nodeId)->velocity.y = (float)yVelocity; } void Silkworm_NodeSetAcceleration(double nodeId, double xAcceleration, double yAcceleration) { LookupNode(nodeId)->acceleration.x = (float)xAcceleration; LookupNode(nodeId)->acceleration.y = (float)yAcceleration; } void Silkworm_NodeSetDestroyable(double nodeId) { LookupNode(nodeId)->destroyable = true; } void Silkworm_ClothNodePin(double clothId, double i, double j) { Silkworm_Cloth* cloth = LookupCloth(clothId); Silkworm_Node* node = cloth->nodes[(uint64_t)i][(uint64_t)j]; if (node != NULL) { node->pinned = true; } } void Silkworm_ClothNodeUnpin(double clothId, double i, double j) { Silkworm_Cloth* cloth = LookupCloth(clothId); Silkworm_Node* node = cloth->nodes[(uint64_t)i][(uint64_t)j]; if (node != NULL) { node->pinned = false; } } void Silkworm_ClothNodeDestroy(double clothId, double i, double j) { Silkworm_Cloth* cloth = LookupCloth(clothId); Silkworm_Node* node = cloth->nodes[(uint64_t)i][(uint64_t)j]; if (node != NULL) { Silkworm_DestroyNode(node->id); } } double Silkworm_CreateLink(double aId, double bId, double distance, double tearThreshold) { Silkworm_Node *nodeA = LookupNode(aId); Silkworm_Node *nodeB = LookupNode(bId); uint64_t id; Silkworm_Link* link = malloc(sizeof(Silkworm_Link)); if (context->linkIndexStackCount > 0) { id = context->linkIndexStack[context->linkIndexStackCount - 1]; context->linkIndexStackCount -= 1; } else { id = context->linkCount; context->links = realloc(context->links, sizeof(Silkworm_Link*) * (context->linkCount + 1)); 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; } double Silkworm_CreateCloth(double xPosition, double yPosition, double width, double height, double mass, double friction, double windFactor, double tearThreshold) { int32_t i, j, k, m; Silkworm_Cloth* cloth = malloc(sizeof(Silkworm_Cloth)); cloth->horizontalNodeCount = ((uint32_t)width / context->clothDensity) + 1; cloth->verticalNodeCount = ((uint32_t)height / context->clothDensity) + 1; cloth->nodes = malloc(sizeof(Silkworm_Node**) * cloth->horizontalNodeCount); 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 < NUM_NODE_TRIANGLE_HASH_BUCKETS; i += 1) { cloth->nodeHash.buckets[i].elements = NULL; cloth->nodeHash.buckets[i].count = 0; } for (i = 0; i < NUM_LINK_TRIANGLE_HASH_BUCKETS; i += 1) { cloth->linkHash.buckets[i].elements = NULL; cloth->linkHash.buckets[i].count = 0; } for (i = 0; i < cloth->horizontalNodeCount; i += 1) { cloth->nodes[i] = malloc(sizeof(Silkworm_Node*) * cloth->verticalNodeCount); for (j = 0; j < cloth->verticalNodeCount; j += 1) { uint64_t nodeId = (uint64_t) Silkworm_CreateNode(xPosition + i * context->clothDensity, yPosition + j * context->clothDensity, mass, friction, 1, 1.0, windFactor); Silkworm_Node *node = LookupNode(nodeId); cloth->nodes[i][j] = node; if (j == 0) { node->pinned = true; } node->destroyable = true; node->nodeParentType = ClothParent; node->parent.clothReference.clothId = cloth->id; node->parent.clothReference.horizontalIndex = i; node->parent.clothReference.verticalIndex = j; } } cloth->triangles = malloc(sizeof(Silkworm_Triangle*) * cloth->horizontalNodeCount * cloth->verticalNodeCount * 2); uint32_t triangleIndex = 0; for (i = 0; i < cloth->horizontalNodeCount; i += 1) { for (j = 0; j < cloth->verticalNodeCount; j += 1) { if (i + 1 < cloth->horizontalNodeCount && j + 1 < cloth->verticalNodeCount) { cloth->triangles[triangleIndex] = malloc(sizeof(Silkworm_Triangle)); cloth->triangles[triangleIndex]->a = cloth->nodes[i][j]; cloth->triangles[triangleIndex]->b = cloth->nodes[i + 1][j]; cloth->triangles[triangleIndex]->c = cloth->nodes[i][j + 1]; cloth->triangles[triangleIndex]->orientation = UpperLeft; cloth->triangles[triangleIndex]->aHorizontalIndex = i; cloth->triangles[triangleIndex]->aVerticalIndex = j; cloth->triangles[triangleIndex]->bHorizontalIndex = i + 1; cloth->triangles[triangleIndex]->bVerticalIndex = j; cloth->triangles[triangleIndex]->cHorizontalIndex = i; cloth->triangles[triangleIndex]->cVerticalIndex = j + 1; NodeTriangleHashTable_Insert(&cloth->nodeHash, cloth->nodes[i][j], triangleIndex); NodeTriangleHashTable_Insert(&cloth->nodeHash, cloth->nodes[i + 1][j], triangleIndex); NodeTriangleHashTable_Insert(&cloth->nodeHash, cloth->nodes[i][j + 1], triangleIndex); triangleIndex += 1; } if (i - 1 >= 0 && j - 1 >= 0) { cloth->triangles[triangleIndex] = malloc(sizeof(Silkworm_Triangle)); cloth->triangles[triangleIndex]->a = cloth->nodes[i][j]; cloth->triangles[triangleIndex]->b = cloth->nodes[i - 1][j]; cloth->triangles[triangleIndex]->c = cloth->nodes[i][j - 1]; cloth->triangles[triangleIndex]->orientation = BottomRight; cloth->triangles[triangleIndex]->aHorizontalIndex = i; cloth->triangles[triangleIndex]->aVerticalIndex = j; cloth->triangles[triangleIndex]->bHorizontalIndex = i - 1; cloth->triangles[triangleIndex]->bVerticalIndex = j; cloth->triangles[triangleIndex]->cHorizontalIndex = i; cloth->triangles[triangleIndex]->cVerticalIndex = j - 1; NodeTriangleHashTable_Insert(&cloth->nodeHash, cloth->nodes[i][j], triangleIndex); NodeTriangleHashTable_Insert(&cloth->nodeHash, cloth->nodes[i - 1][j], triangleIndex); NodeTriangleHashTable_Insert(&cloth->nodeHash, cloth->nodes[i][j - 1], triangleIndex); triangleIndex += 1; } } } cloth->triangleCount = triangleIndex; for (i = 0; i < cloth->horizontalNodeCount; i += 1) { for (j = 0; j < cloth->verticalNodeCount; j += 1) { if (i - 1 >= 0) { double linkId = Silkworm_CreateLink((double)cloth->nodes[i - 1][j]->id, (double)cloth->nodes[i][j]->id, context->clothDensity, tearThreshold); uint32_t indexArrayOneCount = 0; uint32_t* indexArrayOne = NodeTriangleHashTable_Fetch(&cloth->nodeHash, cloth->nodes[i - 1][j], &indexArrayOneCount); uint32_t indexArrayTwoCount = 0; uint32_t* indexArrayTwo = NodeTriangleHashTable_Fetch(&cloth->nodeHash, cloth->nodes[i][j], &indexArrayTwoCount); for (k = 0; k < indexArrayOneCount; k += 1) { uint32_t triangleIndex = indexArrayOne[k]; for (m = 0; m < indexArrayTwoCount; m += 1) { if (indexArrayTwo[m] == triangleIndex) { LinkTriangleHashTable_Insert(&cloth->linkHash, context->links[(uint64_t)linkId], triangleIndex); } } } } if (j - 1 >= 0) { double linkId = Silkworm_CreateLink((double)cloth->nodes[i][j - 1]->id, (double)cloth->nodes[i][j]->id, context->clothDensity, tearThreshold); uint32_t indexArrayOneCount = 0; uint32_t* indexArrayOne = NodeTriangleHashTable_Fetch(&cloth->nodeHash, cloth->nodes[i][j - 1], &indexArrayOneCount); uint32_t indexArrayTwoCount = 0; uint32_t* indexArrayTwo = NodeTriangleHashTable_Fetch(&cloth->nodeHash, cloth->nodes[i][j], &indexArrayTwoCount); for (k = 0; k < indexArrayOneCount; k += 1) { uint32_t triangleIndex = indexArrayOne[k]; for (m = 0; m < indexArrayTwoCount; m += 1) { if (indexArrayTwo[m] == triangleIndex) { LinkTriangleHashTable_Insert(&cloth->linkHash, context->links[(uint64_t)linkId], triangleIndex); } } } } } } return (double)cloth->id; } /* FIXME: this is very unsafe */ double Silkworm_ClothHorizontalNodeCount(double clothId) { return context->cloths[(uint32_t)clothId]->horizontalNodeCount; } double Silkworm_ClothVerticalNodeCount(double clothId) { return context->cloths[(uint32_t)clothId]->verticalNodeCount; } void Silkworm_ClothFillNodeDataBuffer(double clothId) { uint32_t i, j; uint8_t* bufferAddress = context->currentBufferAddress; Silkworm_Cloth* cloth = LookupCloth(clothId); uint8_t active = 0; uint8_t pinned = 0; for (i = 0; i < cloth->horizontalNodeCount; i += 1) { for (j = 0; j < cloth->verticalNodeCount; j += 1) { active = cloth->nodes[i][j] != NULL; memcpy(bufferAddress, &active, sizeof(uint8_t)); bufferAddress += sizeof(uint8_t); pinned = 0; if (cloth->nodes[i][j] != NULL) { pinned = cloth->nodes[i][j]->pinned; } memcpy(bufferAddress, &pinned, sizeof(uint8_t)); bufferAddress += sizeof(uint8_t); } } } void Silkworm_ApplyWind(double xSpeed, double ySpeed) { uint32_t i; Silkworm_Node* node; for (i = 0; i < context->nodeCount; i += 1) { node = LookupNode(i); if (node != NULL && !node->pinned) { node->position.x += (0.5f * sinf(context->timeElapsed * 4 + node->position.y / 2) + 0.5f) * (float)xSpeed * 0.05f * node->windFactor; node->position.y += (0.5f * sinf(context->timeElapsed * 4 + node->position.x / 3) + 0.5f) * (float)ySpeed * 0.05f * node->windFactor; } } } void Silkworm_SetBuffer(const char* bufferId) { context->currentBufferAddress = (uint8_t*)bufferId; } /* pattern is x, y position then color + alpha then UV position */ double Silkworm_ClothFillTriangleBuffer(double clothId, double leftUV, double widthUV, double topUV, double heightUV) { uint32_t i, triangleCount; uint8_t* bufferAddress = context->currentBufferAddress; Silkworm_Cloth* cloth = LookupCloth(clothId); Silkworm_Color color; color.r = 255; color.g = 255; color.b = 255; color.a = 255; triangleCount = 0; for (i = 0; i < cloth->triangleCount; i += 1) { if (cloth->triangles[i] != NULL) { if (cloth->triangles[i]->orientation == UpperLeft) { float left = (float)leftUV + (float)widthUV * ((float)cloth->triangles[i]->aHorizontalIndex / (cloth->horizontalNodeCount - 1)); float right = (float)leftUV + (float)widthUV * ((float)cloth->triangles[i]->bHorizontalIndex / (cloth->horizontalNodeCount - 1)); float top = (float)topUV + (float)heightUV * ((float)cloth->triangles[i]->aVerticalIndex / (cloth->verticalNodeCount - 1)); float bottom = (float)topUV + (float)heightUV * ((float)cloth->triangles[i]->cVerticalIndex / (cloth->verticalNodeCount - 1)); memcpy(bufferAddress, &cloth->triangles[i]->a->position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &left, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &top, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &cloth->triangles[i]->b->position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &right, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &top, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &cloth->triangles[i]->c->position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &left, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &bottom, sizeof(float)); bufferAddress += sizeof(float); triangleCount += 1; } else if (cloth->triangles[i]->orientation == BottomRight) { float left = (float)leftUV + (float)widthUV * ((float)cloth->triangles[i]->bHorizontalIndex / (cloth->horizontalNodeCount - 1)); float right = (float)leftUV + (float)widthUV * ((float)cloth->triangles[i]->aHorizontalIndex / (cloth->horizontalNodeCount - 1)); float top = (float)topUV + (float)heightUV * ((float)cloth->triangles[i]->cVerticalIndex / (cloth->verticalNodeCount - 1)); float bottom = (float)topUV + (float)heightUV * ((float)cloth->triangles[i]->aVerticalIndex / (cloth->verticalNodeCount - 1)); memcpy(bufferAddress, &cloth->triangles[i]->a->position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &right, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &bottom, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &cloth->triangles[i]->b->position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &left, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &bottom, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &cloth->triangles[i]->c->position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &right, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &top, sizeof(float)); bufferAddress += sizeof(float); triangleCount += 1; } } } return (double)triangleCount * 3; } double Silkworm_RopeAddNode(double ropeId, double xPosition, double yPosition, double length) { Silkworm_Rope *rope = LookupRope(ropeId); if (rope != NULL) { uint64_t nodeId = (uint64_t) Silkworm_CreateNode(xPosition, yPosition, rope->nodeMass, rope->friction, 1.0, rope->pushFactor, rope->windFactor); Silkworm_Node* node = LookupNode(nodeId); rope->nodes = realloc(rope->nodes, sizeof(Silkworm_Node*) * (rope->nodeCount + 1)); rope->nodes[rope->nodeCount] = node; rope->nodeCount += 1; node->destroyable = false; node->nodeParentType = RopeParent; node->parent.ropeReference.ropeId = rope->id; node->parent.ropeReference.index = rope->nodeCount - 1; Silkworm_CreateLink((double)rope->nodes[rope->nodeCount - 2]->id, (double)rope->nodes[rope->nodeCount - 1]->id, length, rope->tearThreshold); return nodeId; } else { return -1; } } double Silkworm_RopeVerticalLength(double ropeId) { Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL) { Silkworm_Node* startNode = rope->nodes[0]; Silkworm_Node* endNode = rope->nodes[rope->nodeCount - 1]; if (startNode != NULL && endNode != NULL) { /* return the distance between the end node Y and the start Y */ return fabs(endNode->position.y - startNode->position.y); } } return -1; } double Silkworm_RopeNearestX(double ropeId, double xPosition, double yPosition) { uint32_t i; float nearestSqrDistance, nearestX; nearestSqrDistance = 9999999999.0f; nearestX = 9999999999.0f; Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL) { for (i = 0; i < rope->nodeCount; i += 1) { Silkworm_Node* node = rope->nodes[i]; if (node != NULL) { float yDistance = node->position.y - yPosition; float xDistance = node->position.x - xPosition; float sqrDistance = xDistance * xDistance + yDistance * yDistance; if (sqrDistance < nearestSqrDistance) { nearestSqrDistance = sqrDistance; nearestX = node->position.x; } } } } return nearestX; } double Silkworm_RopeNearestY(double ropeId, double xPosition, double yPosition) { uint32_t i; float nearestSqrDistance, nearestY; nearestSqrDistance = 9999999999.0f; nearestY = 9999999999.0f; Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL) { for (i = 0; i < rope->nodeCount; i += 1) { Silkworm_Node* node = rope->nodes[i]; if (node != NULL) { float yDistance = node->position.y - yPosition; float xDistance = node->position.x - xPosition; float sqrDistance = xDistance * xDistance + yDistance * yDistance; if (sqrDistance < nearestSqrDistance) { nearestSqrDistance = sqrDistance; nearestY = node->position.y; } } } } return nearestY; } double Silkworm_RopeNodeCount(double ropeId) { Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL) { return rope->nodeCount; } return -1; } double Silkworm_RopeNodeXPosition(double ropeId, double nodeIndex) { Silkworm_Rope *rope = LookupRope(ropeId); if (rope != NULL && nodeIndex < rope->nodeCount) { return rope->nodes[(uint32_t)nodeIndex]->position.x; } return -99999999999; } double Silkworm_RopeNodeYPosition(double ropeId, double nodeIndex) { Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL && nodeIndex < rope->nodeCount) { return rope->nodes[(uint32_t)nodeIndex]->position.y; } return -99999999999; } double Silkworm_RopeNodeXSpeed(double ropeId, double nodeIndex) { Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL && nodeIndex < rope->nodeCount) { return rope->nodes[(uint32_t)nodeIndex]->velocity.x; } return 0; } double Silkworm_RopeNodeYSpeed(double ropeId, double nodeIndex) { Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL && nodeIndex < rope->nodeCount) { return rope->nodes[(uint32_t)nodeIndex]->velocity.y; } return 0; } void Silkworm_UnpinRope(double ropeId) { Silkworm_Rope* rope = LookupRope(ropeId); if (rope != NULL) { Silkworm_Node* node = rope->nodes[0]; if (node != NULL) { node->pinned = false; } } } void Silkworm_DeleteEndNode(double ropeId) { Silkworm_Rope *rope = LookupRope(ropeId); if (rope != NULL && rope->nodeCount > 0) { Silkworm_Node* node = rope->nodes[rope->nodeCount - 1]; if (node != NULL) { Silkworm_DestroyNode(node->id); rope->nodeCount -= 1; } } } double Silkworm_RopeCreate(double xPosition, double yPosition, double mass, double friction, double windFactor, double pushFactor, double tearThreshold) { Silkworm_Rope* rope = malloc(sizeof(Silkworm_Rope)); uint64_t id; if (context->ropeIndexStackCount > 0) { id = context->ropeIndexStack[context->ropeIndexStackCount - 1]; context->ropeIndexStackCount -= 1; context->ropes[id] = rope; } else { id = context->ropeCount; context->ropes = realloc(context->ropes, sizeof(Silkworm_Rope*) * (context->ropeCount + 1)); context->ropes[id] = rope; context->ropeCount += 1; } rope->id = id; rope->nodeMass = mass; rope->friction = friction; rope->windFactor = windFactor; rope->tearThreshold = tearThreshold; rope->pushFactor = pushFactor; uint64_t nodeId = (uint64_t) Silkworm_CreateNode(xPosition, yPosition, rope->nodeMass, rope->friction, 1.0, rope->pushFactor, rope->windFactor); Silkworm_Node* node = LookupNode(nodeId); rope->nodes = malloc(sizeof(Silkworm_Node*)); rope->nodeCount = 1; rope->nodes[0] = node; node->pinned = true; node->destroyable = false; node->nodeParentType = RopeParent; node->parent.ropeReference.ropeId = rope->id; node->parent.ropeReference.index = 0; return (double)rope->id; } void Silkworm_RopeDestroy(double ropeId) { uint32_t i; Silkworm_Rope *rope = LookupRope(ropeId); if (rope != NULL) { if (context->ropeIndexStackCount >= context->ropeIndexStackCapacity) { context->ropeIndexStackCapacity *= 2; context->ropeIndexStack = realloc(context->ropeIndexStack, sizeof(uint64_t) * context->ropeIndexStackCapacity); } context->ropeIndexStack[context->ropeIndexStackCount] = rope->id; context->ropeIndexStackCount += 1; for (i = 0; i < rope->nodeCount; i += 1) { if (rope->nodes[i] != NULL) { Silkworm_DestroyNode((double)rope->nodes[i]->id); } } free(rope->nodes); context->ropes[rope->id] = NULL; free(rope); } } double Silkworm_RopeRequiredBufferSize(double ropeId) { Silkworm_Rope* rope = LookupRope(ropeId); return (double)(rope->nodeCount - 1) * 6 * (sizeof(Silkworm_Vector2) + sizeof(Silkworm_Color) + (sizeof(float) * 2)); } double Silkworm_RopeFillBuffer(double ropeId, double width, double leftUV, double widthUV, double topUV, double heightUV) { uint32_t i, vertexCount; Silkworm_Rope* rope = LookupRope(ropeId); uint8_t* bufferAddress = context->currentBufferAddress; Silkworm_Color color; color.r = 255; color.g = 255; color.b = 255; color.a = 255; vertexCount = 0; for (i = 0; i < rope->nodeCount - 1; i += 1) { Silkworm_Node* nodeOne = rope->nodes[i]; Silkworm_Node* nodeTwo = rope->nodes[i + 1]; if (nodeOne == NULL || nodeTwo == NULL) { continue; } Silkworm_Vector2 forwardDirection; forwardDirection.x = nodeTwo->position.x - nodeOne->position.x; forwardDirection.y = nodeTwo->position.y - nodeOne->position.y; float forwardLength = sqrtf(forwardDirection.x * forwardDirection.x + forwardDirection.y * forwardDirection.y); forwardDirection = Vector2_Normalize(forwardDirection); Silkworm_Vector2 upDirection = Vector2_Rotate(forwardDirection, PI / 2); Silkworm_Vector2 topLeft; topLeft.x = nodeOne->position.x + upDirection.x * width / 2; topLeft.y = nodeOne->position.y + upDirection.y * width / 2; Silkworm_Vector2 bottomLeft; bottomLeft.x = nodeOne->position.x - upDirection.x * width / 2; bottomLeft.y = nodeOne->position.y - upDirection.y * width / 2; Silkworm_Vector2 topRight; topRight.x = topLeft.x + forwardDirection.x * forwardLength; topRight.y = topLeft.y + forwardDirection.y * forwardLength; Silkworm_Vector2 bottomRight; bottomRight.x = bottomLeft.x + forwardDirection.x * forwardLength; bottomRight.y = bottomLeft.y + forwardDirection.y * forwardLength; float uvLeft = (float)leftUV; float uvRight = (float)leftUV + (float)widthUV; float uvTop = (float)topUV; float uvBottom = (float)topUV + (float)heightUV; /* top left triangle */ memcpy(bufferAddress, &topLeft, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &uvLeft, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &uvTop, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &topRight, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &uvRight, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &uvTop, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &bottomLeft, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &uvLeft, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &uvBottom, sizeof(float)); bufferAddress += sizeof(float); /* bottom right triangle */ memcpy(bufferAddress, &bottomLeft, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &uvLeft, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &uvBottom, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &topRight, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &uvRight, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &uvTop, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &bottomRight, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); memcpy(bufferAddress, &uvRight, sizeof(float)); bufferAddress += sizeof(float); memcpy(bufferAddress, &uvBottom, sizeof(float)); bufferAddress += sizeof(float); vertexCount += 6; } return (double)vertexCount; } /* in bytes */ double Silkworm_GetEditorBufferRequiredSize() { return 72 * context->nodeCount + 72 * context->linkCount; } /* position, color */ double Silkworm_FillEditorBuffer() { uint32_t i, triangleCount; uint8_t* bufferAddress = context->currentBufferAddress; Silkworm_Vector2 position; position.x = 0; position.y = 0; Silkworm_Color color; color.r = 0; color.g = 0; color.b = 255; color.a = 255; triangleCount = 0; /* draw links */ for (i = 0; i < context->linkCount; i += 1) { Silkworm_Link* link = context->links[i]; if (link != NULL) { /* top left triangle */ float left, right, top, bottom; if (link->a->position.y < link->b->position.y) { top = link->a->position.y; bottom = link->b->position.y; } else { top = link->b->position.y; bottom = link->a->position.y; } if (link->a->position.x < link->b->position.x) { left = link->a->position.x; right = link->b->position.x; } else { left = link->b->position.x; right = link->a->position.x; } /* upper left triangle */ position.x = left; position.y = top; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = right; position.y = top; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = left; position.y = bottom; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); triangleCount += 1; /* bottom right triangle */ position.x = left; position.y = bottom; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = right; position.y = top; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = right; position.y = bottom; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); triangleCount += 1; } } color.r = 255; color.b = 0; /* draw nodes */ for (i = 0; i < context->nodeCount; i += 1) { Silkworm_Node* node = context->nodes[i]; if (node != NULL) { if (node->pinned) { color.r = 255; color.g = 255; color.b = 0; } else { color.r = 255; color.g = 0; color.b = 0; } /* top left triangle */ position.x = node->position.x - node->radius; position.y = node->position.y - node->radius; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = node->position.x + node->radius; position.y = node->position.y - node->radius; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = node->position.x - node->radius; position.y = node->position.y + node->radius; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); triangleCount += 1; /* bottom right triangle */ position.x = node->position.x - node->radius; position.y = node->position.y + node->radius; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = node->position.x + node->radius; position.y = node->position.y - node->radius; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); position.x = node->position.x + node->radius; position.y = node->position.y + node->radius; memcpy(bufferAddress, &position, sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); memcpy(bufferAddress, &color, sizeof(Silkworm_Color)); bufferAddress += sizeof(Silkworm_Color); triangleCount += 1; } } return (double)triangleCount * 3; } void Silkworm_PushNodesInRadius(double x, double y, double radius, double xDirection, double yDirection) { /* TODO: spatial hash implementation */ uint32_t i; for (i = 0; i < context->nodeCount; i += 1) { Silkworm_Node* node = context->nodes[i]; if (node != NULL && !node->pinned) { 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) { node->position.x += (float)xDirection * node->pushFactor; node->position.y += (float)yDirection * node->pushFactor; } } } } void Silkworm_PinNodesInRadius(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) { node->pinned = true; } } } } void Silkworm_UnpinNodesInRadius(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) { node->pinned = false; } } } } 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 && node->destroyable) { 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); if (context->nodeDestructionDataCount >= context->nodeDestructionDataCapacity) { context->nodeDestructionDataCapacity *= 2; context->nodeDestructionData = realloc(context->nodeDestructionData, sizeof(Silkworm_Vector2) * context->nodeDestructionDataCapacity); } context->nodeDestructionData[context->nodeDestructionDataCount] = node->position; context->nodeDestructionDataCount += 1; } } } Silkworm_PerformDestroys(); } double Silkworm_DestroyClothInRadius(double x, double y, double radius) { /* TODO: spatial hash implementation */ uint32_t i; int32_t clothId = -1; 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) { if (node->nodeParentType == ClothParent) { Silkworm_Cloth* cloth = LookupCloth(node->parent.clothReference.clothId); if (cloth != NULL) { clothId = cloth->id; break; } } } } } if (clothId != -1) { Silkworm_DestroyCloth((double)clothId); } Silkworm_PerformDestroys(); return clothId; } double Silkworm_NodeDestructionDataBufferRequiredSize() { return context->nodeDestructionDataCount * (double)sizeof(Silkworm_Vector2); } double Silkworm_FillNodeDestructionDataBuffer() { uint32_t i; uint8_t* bufferAddress = context->currentBufferAddress; for (i = 0; i < context->nodeDestructionDataCount; i += 1) { memcpy(bufferAddress, &context->nodeDestructionData[i], sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); } return (double)i; } double Silkworm_LinkDestructionDataBufferRequiredSize() { return context->linkDestructionDataCount * (double)sizeof(Silkworm_Vector2); } double Silkworm_FillLinkDestructionDataBuffer() { uint32_t i; uint8_t* bufferAddress = context->currentBufferAddress; for (i = 0; i < context->linkDestructionDataCount; i += 1) { memcpy(bufferAddress, &context->linkDestructionData[i], sizeof(Silkworm_Vector2)); bufferAddress += sizeof(Silkworm_Vector2); } return (double)i; } 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]); } } for (i = 0; i < context->ropeCount; i += 1) { if (context->ropes[i] != NULL) { Silkworm_RopeDestroy((double)i); } } Silkworm_PerformDestroys(); context->timeElapsed = 0; } void Silkworm_Finish() { Silkworm_ClearAll(); free(context->nodes); free(context->links); free(context->cloths); free(context->ropes); free(context->nodeIndexStack); free(context->linkIndexStack); free(context->clothIndexStack); free(context->ropeIndexStack); free(context->nodeDestructionData); free(context->linkDestructionData); free(context); context = NULL; }