Datanörderier

Jag började med Basic för att sen hoppa direkt över till Pascal och inline asm. Visst kan jag ibland sakna inline asm och jag gjorde ju för något år sedan en CPU som enbart tog maskinkod så jag har fått mitt av den varan också. Men jag kan inte påstå att jag vill skriva mer än innerloopar i asm.. skulle vara om man kodar på Amiga eller något där varje klockcykel räknas.

Modern C++ -programmering är bättre strukturerad och man kan göra mycket mer på samma tid. Moderna kompilatorer kickar en på asm också så det bara skriker om det. Jag har inte gjort något modernt test av att skriva innerloopar (tänk polygonfyllare eller linjeritare) men det vore lite kul att göra ett test av det igen…

Jag programmerar ju enbart för skojs skull och har gjort i 37 år. När jag jobbade med det tog det bort allt det roliga. Saker skule bara bli färdiga i tid kändes det som.

Känns bra att hittat tillbaka lite och jag är bättre än någonsin.

ChatGPT använder jag mest för komplexa matematiska funktioner, t.ex räkna ut catmull rom TCB splines:

void CurveBase::evaluate(const float pTime, float* pValue) {
    const std::vector<Key>& keys = container().keys();
    const size_t key_size = keys.size();
    assert(key_size == mValue.size());

    if (key_size == 0) {
        memset(pValue, 0, sizeof(float) * mDimensions);
        return;
    }

    // 1 nyckel eller före första -> returnera första
    if (key_size == 1 || pTime <= keys.front().t) {
        memcpy(pValue, mValue.front().value, sizeof(float) * mDimensions);
        return;
    }

    // efter sista -> returnera sista
    if (pTime >= keys.back().t) {
        memcpy(pValue, mValue.back().value, sizeof(float) * mDimensions);
        return;
    }

    // Hitta första index där t >= pTime, med bounds-skydd
    uint32_t offset = 0;
    while (offset < key_size && keys[offset].t < pTime) ++offset;
    // här gäller: 1 <= offset <= key_size-1 (pga tidiga returer ovan)

    // === Linear (oförändrad, men robust) ===
    if (mType == Interpolation::Linear) {
        const int i0 = int(offset) - 1;
        const int i1 = int(offset);

        const float t0 = keys[i0].t;
        const float t1 = keys[i1].t;
        const float td = t1 - t0;
        const float percent = (td != 0.0f) ? ( (pTime - t0) / td ) : 0.0f;

        for (size_t dim = 0; dim < mDimensions; ++dim) {
            pValue[dim] = mValue[i0].value[dim]
                        + (mValue[i1].value[dim] - mValue[i0].value[dim]) * percent;
        }
        return;
    }

    // === Smooth / CatmullRom — alignat med Linear: segment [offset-1, offset] ===
    if (mType == Interpolation::Smooth || mType == Interpolation::CatmullRom) {
        const int i0 = int(offset) - 1;         // vänster nod
        const int i1 = int(offset);             // höger nod
        const bool first = (i0 == 0);
        const bool last  = (i1 == int(key_size) - 1);

        const float t0 = keys[i0].t;
        const float t1 = keys[i1].t;
        const float td = t1 - t0;
        if (td == 0.0f) {
            memcpy(pValue, mValue[i0].value, sizeof(float) * mDimensions);
            return;
        }
        const float f = (pTime - t0) / td;

        const float tPrev = first ? (t0 - 1.0f) : keys[i0 - 1].t;
        const float tNext = last  ? (t1 + 1.0f) : keys[i1 + 1].t;
        const float denom1 = (t1 - tPrev);
        const float denom2 = (tNext - t0);
        const float tang1_t = (denom1 != 0.0f) ? (td / denom1) : 0.0f;
        const float tang2_t = (denom2 != 0.0f) ? (td / denom2) : 0.0f;

        for (size_t dim = 0; dim < mDimensions; ++dim) {
            const float v1 = mValue[i0].value[dim];
            const float v2 = mValue[i1].value[dim];
            const float v0 = first ? v1 : mValue[i0 - 1].value[dim];
            const float v3 = last  ? v2 : mValue[i1 + 1].value[dim];

            const float m1 = (v2 - v0) * tang1_t; // tangent vid v1
            const float m2 = (v3 - v1) * tang2_t; // tangent vid v2

            const float a = v1;
            const float b = m1;
            const float c = -(m2 - m1 - 3.0f * v2 + 3.0f * v1 + 3.0f * m1);
            const float d = (v2 - v1 - m1 - c);

            const float f2 = f * f;
            pValue[dim] = a + b * f + c * f2 + d * f2 * f;
        }
        return;
    }

    // === B-Spline (uniform) — normalisera pTime till [0,1] och använd degree=3 ===
    if (mType == Interpolation::BSpline) {
        // degree p=3 => knots size = key_size + p + 1
        const int p = 3;
        const int n = int(key_size) - 1;
        if (n < p) { // för få punkter, fallback: returnera närmaste
            memcpy(pValue, mValue[offset - 1].value, sizeof(float) * mDimensions);
            return;
        }

        std::vector<float> knots(key_size + p + 1);
        // Uniform klampad knot-vektor [0..1]
        // 0..0, linjärt, 1..1
        for (int i = 0; i <= n + p + 1; ++i) {
            if (i <= p) knots[i] = 0.0f;
            else if (i >= n + 1) knots[i] = 1.0f;
            else knots[i] = float(i - p) / float(n - p + 1);
        }

        // normalisera tid till [0,1]
        const float tmin = keys.front().t;
        const float tmax = keys.back().t;
        const float u = (tmax == tmin) ? 0.0f : ( (pTime - tmin) / (tmax - tmin) );

        // nollställ output
        memset(pValue, 0, sizeof(float) * mDimensions);

        // antag att CURVE_N(i, p, u, knots) finns och tar u i [0,1]
        for (int i = 0; i <= n; ++i) {
            const float Ni = CURVE_N(i, p, u, knots);
            for (size_t dim = 0; dim < mDimensions; ++dim) {
                pValue[dim] += mValue[i].value[dim] * Ni;
            }
        }
        return;
    }

    // === TCB (Kochanek–Bartels) — segment [offset-1, offset] och bugfixar ===
    if (mType == Interpolation::TCB) {
        const int i0 = int(offset) - 1;   // P1
        const int i1 = int(offset);       // P2
        const bool first = (i0 == 0);
        const bool last  = (i1 == int(key_size) - 1);

        const float t0 = keys[i0].t;
        const float t1 = keys[i1].t;
        const float td = t1 - t0;
        if (td == 0.0f) {
            memcpy(pValue, mValue[i0].value, sizeof(float) * mDimensions);
            return;
        }
        const float f = (pTime - t0) / td;

        for (size_t dim = 0; dim < mDimensions; ++dim) {
            const float P0 = first ? mValue[i0].value[dim] : mValue[i0 - 1].value[dim];
            const float P1 = mValue[i0].value[dim];
            const float P2 = mValue[i1].value[dim];
            const float P3 = last  ? mValue[i1].value[dim] : mValue[i1 + 1].value[dim];

            // Hämta T,C,B per nod (global eller per-key)
            const bool useGlobal1 = mValue[i0].useGlobal;
            const bool useGlobal2 = mValue[i1].useGlobal;

            const float T1 = useGlobal1 ? keys[i0].T : mValue[i0].T;
            const float C1 = useGlobal1 ? keys[i0].C : mValue[i0].C;
            const float B1 = useGlobal1 ? keys[i0].B : mValue[i0].B;

            const float T2 = useGlobal2 ? keys[i1].T : mValue[i1].T;
            const float C2 = useGlobal2 ? keys[i1].C : mValue[i1].C;
            const float B2 = useGlobal2 ? keys[i1].B : mValue[i1].B;

            // Tangenter enligt Kochanek–Bartels
            const float d10 = (P1 - P0);
            const float d21 = (P2 - P1);
            const float d32 = (P3 - P2);

            const float m1 = (1 - T1) * ( (1 + C1) * (1 + B1) * d10 * 0.5f
                                         + (1 - C1) * (1 - B1) * d21 * 0.5f );
            const float m2 = (1 - T2) * ( (1 - C2) * (1 + B2) * d21 * 0.5f
                                         + (1 + C2) * (1 - B2) * d32 * 0.5f );

            // Hermite-koeff
            const float a = P1;
            const float b = m1;
            const float c = -3.0f * P1 + 3.0f * P2 - 2.0f * m1 - m2;
            const float d =  2.0f * P1 - 2.0f * P2 + m1 + m2;

            const float f2 = f * f;
            pValue[dim] = a + b * f + c * f2 + d * f2 * f;
        }
        return;
    }

    // fallback (ska aldrig nås om alla typer täcks)
    memcpy(pValue, mValue.back().value, sizeof(float) * mDimensions);
}

… som resulterar i något sånt här:


(work in progress)

Vad bygger du, @AlbertS ? Är det en nätbaserad realtidsgaming, eller? Server och klient(er)?

Mitt första jobb.
Chefen “Kan du C?”
Jag "Nej "
Chefen stoppar en bok I handen på mig och säger “Lär dig!”
“The C Programming Language” av Kernighan & Richie

Man hade inte så mycket nytta av Pascal och Forth när man kom ut i riktiga livet…

5 gillningar

Synkverktyg för demo. Demo är en “musikvideo” med 3d/effekter-programmering och musik som görs av en musiker. Har man tur har man grafiker som ritar grafik till en också. Se exempel på ett vi kodade på mer eller mindre 24h: https://www.youtube.com/watch?v=3TvovyJxF_k . Vi önskar göra myket bättre demo än så och har egentligen kunskapen. Vi har en som jobbar med 3d-rendering full time, en kille på R&D inom nvidia och jag som plattsättare som programmerar :wink:

Jag tröttnade lite på att inte kunna synka på ett bra sätt så jag kodar en editor som kan redigera kameror och värden på saker i demot. För att göra saker mer intressanta att titta på.

Jag har programmerat ett protokoll över nätverk, en server och ett slags “virtuellt filsystem” som pushar uppdateringar live. Editorn kan köras på n ställen och demot samtidigt på n ställen och allting är i synk med varandra baserat på användarnamn. Så editorn kan spola fram och vips så spolar demot fram lika mycket. Alla förändringar i editorn propageras ut till alla som sitter och meckar med demot samtidigt. Ofta sitter man själv hemma men nära en deadline på ett party kanske en musiker lägger in ny musik, kodarna flyttar sina objekt och kodar effekter, grafikern redigerar kamerorrna… allt sker live och inget krockar. Man behöver inte checka in i git och checka ut och så vidare. Allt flyter på.


Så där kan editorn se ut. Timeline uppe till vänster (tänk filmredigering). Höger om den textredigering av en shader med kompilering och errors så man lätt kan se vad man gör. Nere en nodeditor (som är själva grejen i värdesredigering). Till höger alla properties i vald effekt och till vänster tillgängliga effekter att mata in i timeline och lite settings.

Hela projektet är typ 12000 rader kod som jag gjorde under ett halvår. Sen har vi stått lite stilla ett tag.. samtidigt har en annan kodare gjort 3d-motor och sånt… blir nog demo under nästa år känns det som.

“Shake and bake” i nodsystemet ovan är ytterligare en nivå nodsystem (man kan ha hur många djup som helst):

2 gillningar

Så där ser demosidan ut. Kameran styrs av editorn ovan. Bakgrunden är satt av editorn, objektet i mitten är satt av editorn (en slags resurshantering). Färger etc. Men det är inget avancerat egentligen, det är mer en test av 3d-motorn vs editorn. Dvs vi har tre lager (bakgrund, berg, modell) som sys ihop.

Du uppnår en viss imponeringsfaktor.

Det är mest tröjor och dekaler.

Kvällens meck. Välja olika splines och flytta drag points…

1 gillning

Även, ”varför fungerar det inte?” och ”varför fungerar det?”

1 gillning

Det där, lära ungar python… Dottern hade ett år efter gymnasiet då hon jobbade för sin jordenrunt. Hon fick för sig att nu skulle hon lära sig python, hade just ingen programmering på gymnasiet. Kul tänkte jag, nu får man vara lite mentor… Men… det gick rakt in. Men hon är som jag, fångades inte alls. Och donar med andra grejer istället.

Lite trist det där, ungarna frågade nog inte oss en enda gång om nåt skola. Men klarade sig bra ändå.

Även fortsättningen
“Nu funkar det igen!!”
“Vad gjorde du?”
“Vet inte…”

1 gillning

En av sönerna kom hem en gång och hade frågor om Java i något projekt.
Det utmynnade i ett utbrott från min sida.
“HUR F-N KAN NI INTE HA EN DEBUGGER???”
Ni kan väl inte sitta och programmera med “debug”-utskrifter?
Tom när jag läste Pascal på högskolan -85 hade vi ju en debugger

1 gillning

Det låter svårt, att hitta en editor utan debugger…

Påminner om ett konsultuppdrag jag hade på ett stort svenskt telecom-bolag.

En anställd hade integrerat en extern programvara i deras system, det funkade men han visste inte hur han gjort på någon slags detaljnivå.

När det kom en uppdatering av den externa programvaran fungerade det inte och jag fick den delikata (skittråkiga) uppgiften att tillsammans med den anställde att ta reda på vad han gjort.

3 gillningar

?
Det finns väl hur många editorer som helst utan debugger?

1 gillning

Ja. Editor != IDE.

Jajaja hackers jag menade förstås IDE.

Förvånansvärt många utvecklare rör knappt en debugger, jag ser alltid till att kunna ha en men det är inte särskilt användbart för mina applikationer mer än på sandlådenivå. Helt enkelt för mycket data med fart. Så då får man koda egen övervakning och felsökningssystem. Men skulle aldrig sitta i en miljö utan möjlighet till debugger.

1 gillning

Word!
Normal felsökning i min värld är olika grader av (konfigurerbara) debug/trace-loggar. Köra debugger i den mer traditionella meningen går liksom inte i ett fett realtidssystem där saker sker parallelt över X st CPU, Y st DSP, Z st HW-acc, en bunt interface och med delar av mikrosekunds noggrannhet.
När man väl kan reproducera ett fel så är ju jobbet i princip gjort. Sedan handlar det bara om att luska i de där trace-loggarna så brukar saker kunna kluras ut.

2 gillningar

Ibland är man ganska nöjd med att sitta i ett system som bara skickar data till vanrandra i en lugn och upprepbar takt :smiley:

1 gillning