Silkworm/src/Silkworm.c

2156 lines
55 KiB
C

/* 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 <evan@moonside.games>
*
*/
#include "Silkworm.h"
#include <stdlib.h>
#include <string.h>
#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;
}