Created
March 31, 2019 01:05
-
-
Save abextm/6b61ebec38e6b89c5b097856f0b16319 to your computer and use it in GitHub Desktop.
crab animation writer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"bytes" | |
"encoding/binary" | |
"io" | |
"os" | |
"github.com/natefinch/atomic" | |
) | |
type Joint uint | |
type Frame struct { | |
id int | |
Map map[Joint][]int | |
} | |
func frame(m map[Joint][]int) *Frame { | |
return &Frame{ | |
Map: m, | |
} | |
} | |
const UNSET = 0x1FFFF | |
const U = UNSET | |
type SequenceStep struct { | |
Frame *Frame | |
Length int | |
} | |
var fi = `C:\Users\Abex\Documents\runelite\runelite-client\src\main\resources\crabAnim` | |
var fi2 = fi + "2" | |
func Write(steps []SequenceStep) { | |
out, err := os.Create(fi2) | |
if err != nil { | |
panic(err) | |
} | |
defer out.Close() | |
frameCount := 0 | |
writeFrameCount := writeLater(out, 4, func() { | |
binary.Write(out, binary.BigEndian, uint32(frameCount)) | |
}) | |
sequence := &bytes.Buffer{} | |
for _, step := range steps { | |
f := step.Frame | |
if f.id == 0 { | |
frameCount++ | |
f.id = frameCount | |
length := 0 | |
trailer := &bytes.Buffer{} | |
writeLen := writeLater(out, 4, func() { | |
binary.Write(out, binary.BigEndian, uint32(length)) | |
}) | |
ptrStart, _ := out.Seek(0, os.SEEK_CUR) | |
binary.Write(out, binary.BigEndian, uint16(f.id-1)) | |
max := Joint(0) | |
for i := range f.Map { | |
if i > max { | |
max = i | |
} | |
} | |
binary.Write(out, binary.BigEndian, uint8(max+1)) | |
for i := Joint(0); i <= max; i++ { | |
instr := f.Map[i] | |
bitfield := byte(0) | |
for i, v := range instr { | |
if v == UNSET { | |
continue | |
} | |
if v > 0x3f || v < -0x40 { | |
v += 0xC000 | |
binary.Write(trailer, binary.BigEndian, uint16(v)) | |
} else { | |
v += 0x40 | |
trailer.WriteByte(uint8(v)) | |
} | |
bitfield |= 1 << uint(i) | |
} | |
binary.Write(out, binary.BigEndian, bitfield) | |
} | |
io.Copy(out, trailer) | |
ptrEnd, _ := out.Seek(0, os.SEEK_CUR) | |
length = int(ptrEnd - ptrStart) | |
writeLen() | |
} | |
sequence.WriteByte(uint8(f.id - 1)) | |
sequence.WriteByte(uint8(step.Length)) | |
} | |
writeFrameCount() | |
binary.Write(out, binary.BigEndian, uint32(sequence.Len()/2)) | |
io.Copy(out, sequence) | |
out.Close() | |
err = atomic.ReplaceFile(fi2, fi) | |
if err != nil { | |
panic(err) | |
} | |
} | |
func writeLater(fi io.WriteSeeker, gap int, fn func()) func() { | |
to, err := fi.Seek(0, os.SEEK_CUR) | |
if err != nil { | |
panic(err) | |
} | |
fi.Write(make([]byte, gap)) | |
return func() { | |
ret, err := fi.Seek(0, os.SEEK_CUR) | |
if err != nil { | |
panic(err) | |
} | |
_, err = fi.Seek(to, os.SEEK_SET) | |
if err != nil { | |
panic(err) | |
} | |
fn() | |
_, err = fi.Seek(ret, os.SEEK_SET) | |
if err != nil { | |
panic(err) | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "math" | |
func leg(base Joint) Leg { | |
return Leg{ | |
Base: base, | |
Mid: base + 2, | |
Tip: base + 4, | |
} | |
} | |
type Leg struct { | |
Base Joint | |
Mid Joint | |
Tip Joint | |
} | |
const ( | |
LeftEyeRot Joint = 1 | |
LeftEyeScale Joint = 2 | |
LeftEyeMid Joint = 4 | |
LeftEyeBase Joint = 6 | |
RightEyeRot Joint = 8 | |
RightEyeScale Joint = 9 | |
RightEyeMid Joint = 11 | |
RightEyeBase Joint = 13 | |
RightMouthEdge Joint = 15 | |
) | |
var ( | |
LeftLeg3 = leg(17) | |
LeftLeg2 = leg(23) | |
LeftLeg1 = leg(29) | |
RightLeg3 = leg(35) | |
RightLeg2 = leg(41) | |
RightLeg1 = leg(47) | |
) | |
const ( | |
RightShoulder Joint = 53 | |
RightWrist Joint = 55 | |
RightThumb Joint = 57 | |
LeftShoulder Joint = 59 | |
LeftWrist Joint = 61 | |
LeftThumb Joint = 63 | |
BodyRotate Joint = 65 | |
BodyShift Joint = 66 | |
BodyScale Joint = 67 | |
BodyOpacity Joint = 68 | |
) | |
const ( | |
ArmLengthA = 45 | |
ArmLengthB = 48 | |
ArmLengthC = 21 | |
ArmDx = 60 | |
ArmDy = 42 | |
) | |
func main() { | |
frames := make([]*Frame, 5) | |
halfLen := (len(frames) / 2) | |
radius := 24 | |
radiusStep := radius / halfLen | |
height := 28 | |
heightStep := height / halfLen | |
for i := range frames { | |
ci := i - halfLen | |
aci := ci | |
if aci < 0 { | |
aci = -aci | |
} | |
f := &Frame{Map: map[Joint][]int{}} | |
frames[i] = f | |
f.Map[BodyScale] = []int{96, 96, 96} | |
dx := radius - (i * radiusStep) | |
dy := (halfLen - aci) * heightStep | |
f.Map[BodyShift] = []int{dx, -dy} | |
tip := j2r(128 + 12) | |
f.Map[LeftLeg1.Tip] = []int{U, U, 152 - r2j(tip)} | |
var elbowDelta float64 | |
var simpleJointLen float64 | |
var shoulderToFloor float64 | |
var shoulderToFloorAngle float64 | |
{ | |
A := tip | |
const b = ArmLengthB | |
const c = ArmLengthC | |
a := math.Sqrt(b*b + c*c - 2*b*c*math.Cos(A)) | |
C := math.Asin((math.Sin(A) / a) * c) | |
elbowDelta = C | |
simpleJointLen = a | |
} | |
{ | |
g := float64(.79) | |
a := float64(60/g - float64(dx)) | |
b := float64(42/g + float64(dy)) | |
c := math.Sqrt(a*a + b*b) | |
A := math.Asin(a / c) | |
shoulderToFloor = c | |
shoulderToFloorAngle = A | |
} | |
{ | |
a := shoulderToFloor | |
const b = ArmLengthA | |
c := simpleJointLen | |
A := math.Acos((b*b + c*c - a*a) / (2 * b * c)) | |
C := math.Acos((a*a + b*b - c*c) / (2 * a * b)) | |
f.Map[LeftLeg1.Base] = []int{U, U, -84 + r2j(C+shoulderToFloorAngle)} | |
f.Map[LeftLeg1.Mid] = []int{U, U, 192 + r2j(A+elbowDelta)} | |
} | |
f.Map[LeftEyeBase] = []int{aci * 2, U, ci * 2} | |
f.Map[LeftEyeMid] = f.Map[LeftEyeBase] | |
f.Map[RightEyeBase] = f.Map[LeftEyeBase] | |
f.Map[RightEyeMid] = f.Map[LeftEyeMid] | |
f.Map[RightShoulder] = []int{U, U, 32 - i*4} | |
f.Map[LeftShoulder] = []int{U, U, -16 - i*4} | |
} | |
for i := range frames { | |
frames[i].Map[LeftLeg2.Base] = frames[i].Map[LeftLeg1.Base] | |
frames[i].Map[LeftLeg2.Mid] = frames[i].Map[LeftLeg1.Mid] | |
frames[i].Map[LeftLeg2.Tip] = frames[i].Map[LeftLeg1.Tip] | |
frames[i].Map[LeftLeg3.Base] = frames[i].Map[LeftLeg1.Base] | |
frames[i].Map[LeftLeg3.Mid] = frames[i].Map[LeftLeg1.Mid] | |
frames[i].Map[LeftLeg3.Tip] = frames[i].Map[LeftLeg1.Tip] | |
} | |
for i := range frames { | |
e := len(frames) - 1 | |
frames[e-i].Map[RightLeg1.Base] = invU(frames[i].Map[LeftLeg1.Base]) | |
frames[e-i].Map[RightLeg1.Mid] = invU(frames[i].Map[LeftLeg1.Mid]) | |
frames[e-i].Map[RightLeg1.Tip] = invU(frames[i].Map[LeftLeg1.Tip]) | |
frames[e-i].Map[RightLeg2.Base] = invU(frames[i].Map[LeftLeg1.Base]) | |
frames[e-i].Map[RightLeg2.Mid] = invU(frames[i].Map[LeftLeg1.Mid]) | |
frames[e-i].Map[RightLeg2.Tip] = invU(frames[i].Map[LeftLeg1.Tip]) | |
frames[e-i].Map[RightLeg3.Base] = invU(frames[i].Map[LeftLeg1.Base]) | |
frames[e-i].Map[RightLeg3.Mid] = invU(frames[i].Map[LeftLeg1.Mid]) | |
frames[e-i].Map[RightLeg3.Tip] = invU(frames[i].Map[LeftLeg1.Tip]) | |
} | |
mul := 2 | |
// 24 frames to match 125 bpm | |
// | |
out := []SequenceStep{ | |
{frames[0], 5 * mul}, | |
{frames[1], 3 * mul}, | |
{frames[2], 2 * mul}, | |
{frames[3], 2 * mul}, | |
{frames[4], 5 * mul}, | |
{frames[3], 2 * mul}, | |
{frames[2], 2 * mul}, | |
{frames[1], 3 * mul}, | |
{frames[0], 0}, // Fake the anim smoothing | |
} | |
Write(out) | |
} | |
func invU(a []int) []int { | |
out := make([]int, len(a)) | |
for i, v := range a { | |
if i == 0 { | |
out[i] = v | |
} else { | |
if v == U { | |
out[i] = U | |
} else { | |
out[i] = -v | |
} | |
} | |
} | |
return out | |
} | |
func r2j(a float64) int { | |
return int(a*(128/math.Pi) + .5) | |
} | |
func j2r(j int) float64 { | |
return float64(j) * (math.Pi / 128) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment