I am creating a cloth simulation, and I have decided that the best method is to create a cage made of Parts and SpringConstraints inside of the Workspace service, then use Camera:WorldToScreenPoint()
to generate a wire-frame with edges and vertices inside of the UI. My only problem is being able to generate faces, which require filling in the space in-between edges. So far, the simulation is very realistic, and I do not want to abandon it.
Here is what the simulation looks like so far.
If you want the source code for edges and vertices, here it is. They are all LocalScripts under Frames.
VERTEX:
local RunService = game:GetService("RunService") --RUNSERVICE --CAMERA local camera = workspace.CurrentCamera --PART OF CAGE local vertex = workspace:WaitForChild("vertex1") local point = script.Parent --FRAME --DRAW-- RunService.RenderStepped:Connect(function() --FIND WHERE POINT IS ON SCREEN local vertPos, vertOnScreen = camera:WorldToScreenPoint(vertex.Position) --IS THE POINT ON SCREEN? if vertOnScreen then point.BackgroundTransparency = 0 --VISIBLE point.Size = UDim2.new(0, 10*(10/vertPos.Z), 0, 10*(10/vertPos.Z)) --SET SCALE BASED ON Z point.Position = UDim2.new(0, vertPos.X - (point.Size.X.Offset / 2), 0, vertPos.Y - (point.Size.Y.Offset / 2)) --CENTERED ON POINT IN CAGE else point.BackgroundTransparency = 1 --INVISIBLE end end) --BLAHBLAHIDONTKNO
EDGE:
local RunService = game:GetService("RunService") --RUNSERVICE --CAMERA local camera = workspace.CurrentCamera --PARTS OF CAGE local vertex1 = workspace:WaitForChild("vertex1") local vertex2 = workspace:WaitForChild("vertex2") --FRAME local edge = script.Parent --DRAW-- RunService.RenderStepped:Connect(function() --FIND WHERE POINT IS ON SCREEN local vert1Pos, vert1OnScreen = camera:WorldToScreenPoint(vertex1.Position) local vert2Pos, vert2OnScreen = camera:WorldToScreenPoint(vertex2.Position) --CHECK IF POINT IS ON SCREEN if vert1OnScreen and vert2OnScreen then edge.BackgroundTransparency = 0 --VISIBLE --DISTANCES FOR X AND Y local yDelta = vert2Pos.Y - vert1Pos.Y local xDelta = vert2Pos.X - vert1Pos.X --SET POSITION TO MIDDLE OF TWO POINTS edge.Position = UDim2.new(0, (vert1Pos.X - (edge.Size.X.Offset / 2)) + (xDelta / 2), 0, vert1Pos.Y - (edge.Size.Y.Offset / 2) + (yDelta / 2)) local dist = math.sqrt((xDelta^2)+(yDelta^2)) --DISTANCE BETWEEN TWO POINTS edge.Rotation = math.deg(math.atan(yDelta/xDelta)) --USE XDELTA AND YDELTA TO COMPUTE ANGLE IN DEGREES edge.Size = UDim2.new(0, dist, 0, 5*(5/vert1Pos.Z)) --SET LENGTH TO DISTANCE, SET THICKNESS BASED ON Z DIST else edge.BackgroundTransparency = 1 --INVISIBLE end end) --BLAHBLAHIDONTKNO
How should I approach filling in the faces?
EDIT: Actually, using knowledge from vector graphics, it could be possible to use the three edges as an "enclosed vector curve", and then create a grid of pixels confined to that group of edges, allowing for custom shapes.
Here is what I am talking about.
So far, I have gotten the grid to track to the triangle by taking the minimum and maximum points, and then positioning and scaling the grid based on those points.
Here's the current script for the face. I had to modify the size of each "pixel" from 3 pixels to 5 pixels to reduce lag.
local RunService = game:GetService("RunService") --RUNSERVICE local camera = workspace.CurrentCamera --CAMERA --PARTS OF CAGE local vertex1 = workspace:WaitForChild("vertex1") local vertex2 = workspace:WaitForChild("vertex2") local vertex3 = workspace:WaitForChild("vertex14") --CONTAINER OF PIXELS local face = script.Parent --DRAW-- RunService.RenderStepped:Connect(function() --VERTICES THAT MAKE UP TRI local vert1Pos, vert1OnScreen = camera:WorldToScreenPoint(vertex1.Position) local vert2Pos, vert2OnScreen = camera:WorldToScreenPoint(vertex2.Position) local vert3Pos, vert3OnScreen = camera:WorldToScreenPoint(vertex3.Position) local vertPos = {vert1Pos, vert2Pos, vert3Pos} --TABLE OF POSITIONS --DO NOT APPEAR WHEN POINTS ARE OFF SCREEN if vert1OnScreen and vert2OnScreen and vert3OnScreen then face.BackgroundTransparency = 1 --CONTAINER IS INVISIBLE --FIND MINIMUM AND MAXIMUM POINTS OF TRI local minX = 999999 local maxX = -999999 local minY = 999999 local maxY = -999999 for _, v in pairs(vertPos) do if v.X < minX then minX = v.X end if v.X > maxX then maxX = v.X end if v.Y < minY then minY = v.Y end if v.Y > maxY then maxY = v.Y end end --DETERMINE POSITION AND SIZE OF GRID BASED ON MIN AND MAX face.Position = UDim2.new(0, minX, 0, minY) --local dist = math.sqrt((xDelta^2)+(yDelta^2)) --face.Rotation = math.deg(math.atan(v2Delta/v3Delta)) face.Size = UDim2.new(0, maxX - minX, 0, maxY - minY) --DRAW PIXELS local pixelSize = UDim2.new(0, 5, 0, 5) for i = 0, face.Size.X.Offset do for j = 0, face.Size.Y.Offset do if i % 5 == 0 and j % 5 == 0 then local pixel = Instance.new("Frame", face) pixel.Size = pixelSize pixel.Position = UDim2.new(0, i, 0, j) --pixel.BorderSizePixel = 0 pixel.BackgroundColor3 = Color3.new(255, 255, 255) end end end wait() --REFRESH for _, v in pairs(face:GetChildren()) do if v:IsA("Frame") then v:Destroy() end end else face.BackgroundTransparency = 1 end end) --BLAHBLAHIDONTKNO
Actually, now that I think about it, spamming edges would be the most efficient method, as spamming pixels generates more lag. Thanks, @StreamG0D!
Yeah so this is a bit of an old question, but I think I have a different answer to all those previously posted.
You can draw 2D triangles if you use image labels and a little bit of math.
You can read this post for the math/code
And for the image labels I recommend checking out Quenty's post as opposed to the images I uploaded as they'll get rid of the black line issue that mine have.
Hope this helps!