mirror of
https://github.com/Kravickas/PS4-apps.git
synced 2026-10-17 15:00:54 +02:00
Delete src directory
This commit is contained in:
1 parent
be1d06c8a6
commit
2a0d92fd73
8 files changed
-12696
No files matched your search
@@ -1,74 +0,0 @@
|
||||
/* bmp_loader.h — Load BMP texture into GPU memory as RGBA8 */
|
||||
|
||||
typedef struct {
|
||||
void *pixels; /* RGBA8 pixel data (GPU allocated) */
|
||||
int width, height;
|
||||
unsigned long size; /* total bytes */
|
||||
} BmpTexture;
|
||||
|
||||
static int bmp_load(const char *path, void *(*alloc_fn)(unsigned long, unsigned long), BmpTexture *out) {
|
||||
int fd = sceKernelOpen(path, 0, 0);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
/* Read BMP header (14 bytes) + DIB header (40 bytes) */
|
||||
unsigned char hdr[54];
|
||||
if (sceKernelRead(fd, hdr, 54) < 54) { sceKernelClose(fd); return -2; }
|
||||
|
||||
if (hdr[0] != 'B' || hdr[1] != 'M') { sceKernelClose(fd); return -3; }
|
||||
|
||||
unsigned int data_off = *(unsigned int*)(hdr + 10);
|
||||
int w = *(int*)(hdr + 18);
|
||||
int h = *(int*)(hdr + 22);
|
||||
int bpp = *(unsigned short*)(hdr + 28);
|
||||
int compr = *(unsigned int*)(hdr + 30);
|
||||
int flip = 1; /* BMP is bottom-up by default */
|
||||
if (h < 0) { h = -h; flip = 0; } /* top-down BMP */
|
||||
|
||||
if (compr != 0 || (bpp != 24 && bpp != 32)) { sceKernelClose(fd); return -4; }
|
||||
|
||||
/* Allocate RGBA8 output */
|
||||
unsigned long out_size = (unsigned long)w * h * 4;
|
||||
void *pixels = alloc_fn(out_size, 0x1000);
|
||||
if (!pixels) { sceKernelClose(fd); return -5; }
|
||||
|
||||
/* Read pixel data */
|
||||
sceKernelLseek(fd, data_off, 0);
|
||||
int stride_bmp = ((w * (bpp / 8) + 3) & ~3); /* BMP rows are 4-byte aligned */
|
||||
unsigned long row_sz = stride_bmp;
|
||||
|
||||
/* Read row by row, convert BGR(A) → RGBA, flip if needed */
|
||||
/* Allocate row buffer on heap for large textures */
|
||||
unsigned char *row_buf = (unsigned char*)alloc_fn(stride_bmp + 16, 64);
|
||||
if (!row_buf) { sceKernelClose(fd); return -6; }
|
||||
unsigned char *dst = (unsigned char*)pixels;
|
||||
|
||||
for (int y = 0; y < h; y++) {
|
||||
int src_y = y; /* no flip: bottom-up */
|
||||
sceKernelLseek(fd, data_off + (long)src_y * stride_bmp, 0);
|
||||
int rd = sceKernelRead(fd, row_buf, row_sz);
|
||||
if (rd < (int)(w * (bpp / 8))) break;
|
||||
|
||||
unsigned char *d = dst + (long)y * w * 4;
|
||||
for (int x = 0; x < w; x++) {
|
||||
int sx = x; /* no flip — correct UV mapping */
|
||||
if (bpp == 24) {
|
||||
d[x*4+0] = row_buf[sx*3+2]; /* R */
|
||||
d[x*4+1] = row_buf[sx*3+1]; /* G */
|
||||
d[x*4+2] = row_buf[sx*3+0]; /* B */
|
||||
d[x*4+3] = 255; /* A */
|
||||
} else { /* 32bpp BGRA */
|
||||
d[x*4+0] = row_buf[sx*4+2]; /* R */
|
||||
d[x*4+1] = row_buf[sx*4+1]; /* G */
|
||||
d[x*4+2] = row_buf[sx*4+0]; /* B */
|
||||
d[x*4+3] = row_buf[sx*4+3]; /* A */
|
||||
}
|
||||
}
|
||||
}
|
||||
sceKernelClose(fd);
|
||||
|
||||
out->pixels = pixels;
|
||||
out->width = w;
|
||||
out->height = h;
|
||||
out->size = out_size;
|
||||
return 0;
|
||||
}
|
||||
-8198
File diff suppressed because it is too large.
Load diff
-3071
File diff suppressed because it is too large.
Load diff
@@ -1,126 +0,0 @@
|
||||
#pragma once
|
||||
/* Types come from <stdint.h> which main.c includes before this header.
|
||||
Do NOT re-typedef uint32_t / uint8_t / int64_t here — that conflicts with
|
||||
the OpenOrbis SDK's <stdint.h> when building via the SDK toolchain. */
|
||||
#include <stdint.h>
|
||||
|
||||
extern int sceKernelAllocateDirectMemory(long, long, unsigned long, unsigned long, int, unsigned long*);
|
||||
extern int sceKernelMapDirectMemory(void**, unsigned long, int, int, long, unsigned long);
|
||||
extern unsigned int sceKernelUsleep(unsigned int);
|
||||
extern int sceKernelOpen(const char*, int, unsigned short);
|
||||
extern long sceKernelRead(int, void*, unsigned long);
|
||||
extern long sceKernelWrite(int, const void*, unsigned long);
|
||||
extern int sceKernelFsync(int);
|
||||
extern int sceKernelClose(int);
|
||||
extern long sceKernelLseek(int, long, int);
|
||||
extern uint64_t sceKernelGetProcessTime(void);
|
||||
extern int sceVideoOutOpen(int, int, int, void*);
|
||||
extern void sceVideoOutSetBufferAttribute(void*, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
|
||||
extern int sceVideoOutRegisterBuffers(int, int, void *const*, int, const void*);
|
||||
extern int sceVideoOutSubmitFlip(int, int, uint32_t, int64_t);
|
||||
extern void sceVideoOutSetFlipRate(int, int);
|
||||
/* Flip status — OpenOrbis canonical layout (verified). flipArg lets us wait
|
||||
for a SPECIFIC frame's flip to complete (the proper PS4 sync), and
|
||||
numFlipPending bounds the queue. */
|
||||
typedef struct {
|
||||
uint64_t num;
|
||||
uint64_t ptime;
|
||||
uint64_t stime;
|
||||
int64_t flipArg;
|
||||
uint64_t reserved[2];
|
||||
int32_t numGpuFlipPending;
|
||||
int32_t numFlipPending;
|
||||
int32_t currentBuffer;
|
||||
uint32_t reserved1;
|
||||
} OrbisVideoOutFlipStatus;
|
||||
extern int sceVideoOutGetFlipStatus(int, OrbisVideoOutFlipStatus*);
|
||||
|
||||
/* Vblank status: count (offset 0) = number of vblanks since sceVideoOutOpen.
|
||||
Used to tell whether the display vblank itself stalls at ~8s, or whether the
|
||||
vblank keeps ticking but the flip-completion worker stops being woken. */
|
||||
typedef struct {
|
||||
uint64_t count;
|
||||
uint64_t processTime;
|
||||
uint64_t tsc;
|
||||
uint64_t _reserved;
|
||||
uint8_t flags;
|
||||
uint8_t pad[7];
|
||||
} OrbisVideoOutVblankStatus;
|
||||
extern int sceVideoOutGetVblankStatus(int, OrbisVideoOutVblankStatus*);
|
||||
extern int sceVideoOutUnregisterBuffers(int, int);
|
||||
extern int sceVideoOutClose(int);
|
||||
/* System service event — used to detect the quit/terminate request so we can
|
||||
exit cleanly instead of letting the OS time out (~1 min) and crash.
|
||||
The event struct is large (8192-byte SDK layout); we only read eventType,
|
||||
the first int32. ReceiveEvent returns 0 when an event was dequeued. */
|
||||
extern int sceSystemServiceReceiveEvent(void* event);
|
||||
/* Event-driven flip pacing: register a flip event queue at startup and block on
|
||||
it each frame until the flip completes (canonical PS4 vsync pacing). */
|
||||
typedef uint64_t OrbisKernelEqueue;
|
||||
struct kevent_t { uintptr_t ident; short filter; uint16_t flags; uint32_t fflags;
|
||||
intptr_t data; void* udata; };
|
||||
extern int sceKernelCreateEqueue(OrbisKernelEqueue*, const char*);
|
||||
extern int sceKernelDeleteEqueue(OrbisKernelEqueue);
|
||||
extern int sceKernelWaitEqueue(OrbisKernelEqueue, struct kevent_t*, int, int*, unsigned int*);
|
||||
extern int sceVideoOutAddFlipEvent(OrbisKernelEqueue, int, void*);
|
||||
extern int sceVideoOutDeleteFlipEvent(OrbisKernelEqueue, int);
|
||||
|
||||
/* Buffer flip-done label base; label[i] is at base + i*8. */
|
||||
extern int sceVideoOutGetBufferLabelAddress(int, void**);
|
||||
|
||||
/* Emits a 7-dword WAIT_REG_MEM(label[bufIdx] == 0) into the command buffer.
|
||||
size_dw must be 7 or it returns -1 and emits nothing. */
|
||||
extern int sceGnmInsertWaitFlipDone(uint32_t*, uint32_t, int, int);
|
||||
|
||||
/* Vblank event: the display-hardware vblank IRQ, delivered every 16.6ms
|
||||
independent of the VideoOut flip-worker thread. On a FAT console
|
||||
(sceKernelIsNeoMode()==0) the flip worker polls on a 500ms fallback, so
|
||||
pacing on the flip-completion event degrades to 500ms/frame. The vblank
|
||||
event is the correct, always-running pace source. */
|
||||
extern int sceVideoOutAddVblankEvent(OrbisKernelEqueue, int, void*);
|
||||
extern int sceVideoOutDeleteVblankEvent(OrbisKernelEqueue, int);
|
||||
extern int sceGnmSubmitCommandBuffers(uint32_t, void**, uint32_t*, uint32_t*, uint32_t);
|
||||
/* Canonical submit+flip: patches the prepareFlip packet at the end of the last
|
||||
DCB so the GPU writes the VO buffer label and flips at end-of-pipe (the
|
||||
handshake the standalone sceVideoOutSubmitFlip skipped). */
|
||||
extern int sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void** dcb_addrs,
|
||||
uint32_t* dcb_sizes, void** ccb_addrs,
|
||||
uint32_t* ccb_sizes, int vo_handle,
|
||||
int buf_idx, uint32_t flip_mode,
|
||||
int64_t flip_arg);
|
||||
extern int sceGnmSubmitDone(void);
|
||||
|
||||
/* Returns 1 when the driver's in-flight counter is 0 (GPU quiesced).
|
||||
The game spins on this after SubmitDone before tearing down. */
|
||||
extern int sceGnmAreSubmitsAllowed(void);
|
||||
extern int printf(const char*, ...);
|
||||
extern int scePadInit(void);
|
||||
extern int scePadOpen(int, int, int, void*);
|
||||
extern int scePadRead(int, void*, int);
|
||||
extern int scePadReadState(int, void*);
|
||||
extern int sceUserServiceInitialize(void*);
|
||||
extern int sceUserServiceGetInitialUser(int*);
|
||||
typedef struct { uint32_t format; uint32_t tiling; uint32_t aspect; uint32_t width; uint32_t height; uint32_t pitch; } SceVideoOutBufferAttribute;
|
||||
struct OrbisPadData {
|
||||
uint32_t buttons;
|
||||
uint8_t lx, ly, rx, ry;
|
||||
uint8_t l2, r2;
|
||||
uint8_t _pad0[2];
|
||||
float quat[4], accel[3], angvel[3];
|
||||
uint8_t _rest[200];
|
||||
};
|
||||
#define PAD_L3 0x0002
|
||||
#define PAD_R3 0x0004
|
||||
#define PAD_OPTIONS 0x0008
|
||||
#define PAD_UP 0x0010
|
||||
#define PAD_RIGHT 0x0020
|
||||
#define PAD_DOWN 0x0040
|
||||
#define PAD_LEFT 0x0080
|
||||
#define PAD_L1 0x0400
|
||||
#define PAD_R1 0x0800
|
||||
#define PAD_TRI 0x1000
|
||||
#define PAD_CROSS 0x4000
|
||||
|
||||
/* Memory freeing */
|
||||
extern int sceKernelMunmap(void *addr, unsigned long len);
|
||||
extern int sceKernelReleaseDirectMemory(long phys_addr, unsigned long len);
|
||||
@@ -1,461 +0,0 @@
|
||||
#pragma once
|
||||
/*
|
||||
* obj_loader.h — 3-pass streaming OBJ loader
|
||||
*
|
||||
* NEVER loads the full 2.1GB file. Streams 64MB chunks.
|
||||
* Pass 1: count verts + faces
|
||||
* Pass 2: read positions (162MB)
|
||||
* Pass 3: emit triangulated faces to VB (2.6GB)
|
||||
* Peak: 162MB + 2.6GB + 64MB = 2.82GB
|
||||
*/
|
||||
|
||||
#define OBJ_STRIDE 12
|
||||
#define OBJ_BG_VERTS 6
|
||||
#define OBJ_IDENT_SIZE 64
|
||||
#define OBJ_BG_SUN_SIZE 16
|
||||
#define OBJ_BG_SIZE (OBJ_BG_VERTS * OBJ_STRIDE * 4)
|
||||
#define OBJ_MVP_OFF (OBJ_IDENT_SIZE + OBJ_BG_SUN_SIZE + OBJ_BG_SIZE)
|
||||
#define OBJ_MVP_SIZE 64
|
||||
#define OBJ_SUN_SIZE 16
|
||||
#define OBJ_DATA_OFF (OBJ_MVP_OFF + OBJ_MVP_SIZE + OBJ_SUN_SIZE)
|
||||
#define OBJ_CHUNK (64 * 1024 * 1024)
|
||||
|
||||
typedef struct {
|
||||
void *vb_base; float *verts; uint32_t *ib_base;
|
||||
int num_verts, num_tris, num_indices, indexed;
|
||||
unsigned long vb_size, ib_size;
|
||||
} ObjMesh;
|
||||
|
||||
static void *obj_talloc(unsigned long size, long *phys) {
|
||||
unsigned long a = 0x4000;
|
||||
size = (size + a - 1) & ~(a - 1); if (size < a) size = a;
|
||||
long ph = 0;
|
||||
if (sceKernelAllocateDirectMemory(0, 0x600000000ULL, size, a, 3, &ph) != 0) return 0;
|
||||
void *addr = 0;
|
||||
if (sceKernelMapDirectMemory(&addr, size, 3, 0, ph, a) != 0) return 0;
|
||||
*phys = ph; return addr;
|
||||
}
|
||||
static void obj_tfree(void *addr, long phys, unsigned long size) {
|
||||
unsigned long a = 0x4000;
|
||||
size = (size + a - 1) & ~(a - 1); if (size < a) size = a;
|
||||
if (addr) sceKernelMunmap(addr, size);
|
||||
if (phys >= 0) sceKernelReleaseDirectMemory(phys, size);
|
||||
}
|
||||
|
||||
static float obj_atof(const char **pp, const char *end) {
|
||||
const char *p = *pp;
|
||||
while (p<end&&(*p==' '||*p=='\t')) p++;
|
||||
float s=1; if(p<end&&*p=='-'){s=-1;p++;}else if(p<end&&*p=='+')p++;
|
||||
float v=0; while(p<end&&*p>='0'&&*p<='9'){v=v*10+(*p-'0');p++;}
|
||||
if(p<end&&*p=='.'){p++;float f=.1f;while(p<end&&*p>='0'&&*p<='9'){v+=(*p-'0')*f;f*=.1f;p++;}}
|
||||
if(p<end&&(*p=='e'||*p=='E')){p++;int es=1,ex=0;if(p<end&&*p=='-'){es=-1;p++;}else if(p<end&&*p=='+')p++;while(p<end&&*p>='0'&&*p<='9'){ex=ex*10+(*p-'0');p++;}float m=1;for(int i=0;i<ex;i++)m*=10;v=es>0?v*m:v/m;}
|
||||
*pp=p; return s*v;
|
||||
}
|
||||
static int obj_atoi(const char **pp, const char *end) {
|
||||
const char *p=*pp;while(p<end&&(*p==' '||*p=='\t'))p++;
|
||||
int s=1;if(p<end&&*p=='-'){s=-1;p++;}int v=0;
|
||||
while(p<end&&*p>='0'&&*p<='9'){v=v*10+(*p-'0');p++;}*pp=p;return s*v;
|
||||
}
|
||||
static const char *obj_memchr(const char *s, char c, int n) {
|
||||
for(int i=0;i<n;i++) if(s[i]==c) return s+i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static float obj_sqrtf(float x){if(x<=0)return 0;float r=x;for(int i=0;i<8;i++)r=.5f*(r+x/r);return r;}
|
||||
|
||||
|
||||
/* MTL material table */
|
||||
#define MTL_MAX 32
|
||||
#define MTL_NAME_LEN 48
|
||||
typedef struct { char name[MTL_NAME_LEN]; float r, g, b; } MtlEntry;
|
||||
typedef struct { MtlEntry m[MTL_MAX]; int n; } MtlTable;
|
||||
|
||||
static int mtl_name_eq(const char *a, const char *b) {
|
||||
while (*a && *b && *a == *b) { a++; b++; }
|
||||
return (*a == 0 || *a == '\n' || *a == '\r') && (*b == 0 || *b == '\n' || *b == '\r');
|
||||
}
|
||||
|
||||
static void mtl_load(const char *obj_path, MtlTable *t) {
|
||||
t->n = 0;
|
||||
/* Build MTL path: try .obj → .mtl */
|
||||
char mtl_path[256];
|
||||
int i = 0;
|
||||
while (obj_path[i] && i < 250) { mtl_path[i] = obj_path[i]; i++; }
|
||||
mtl_path[i] = 0;
|
||||
/* Replace .obj → .mtl */
|
||||
if (i > 4 && mtl_path[i-4] == '.') {
|
||||
mtl_path[i-3] = 'm'; mtl_path[i-2] = 't'; mtl_path[i-1] = 'l';
|
||||
}
|
||||
|
||||
int fd = sceKernelOpen(mtl_path, 0, 0);
|
||||
if (fd < 0) return;
|
||||
|
||||
/* Read entire MTL (typically <4KB) */
|
||||
char buf[4096];
|
||||
int len = sceKernelRead(fd, buf, sizeof(buf)-1);
|
||||
sceKernelClose(fd);
|
||||
if (len <= 0) return;
|
||||
buf[len] = 0;
|
||||
|
||||
int cur = -1;
|
||||
const char *lp = buf;
|
||||
while (lp < buf + len) {
|
||||
const char *le = lp;
|
||||
while (le < buf + len && *le != '\n') le++;
|
||||
|
||||
if (lp[0]=='n' && lp[1]=='e' && lp[2]=='w' && lp[3]=='m' && t->n < MTL_MAX) {
|
||||
cur = t->n++;
|
||||
const char *nm = lp + 7;
|
||||
int j = 0;
|
||||
while (j < MTL_NAME_LEN-1 && nm+j < le && nm[j] != '\r' && nm[j] != '\n')
|
||||
{ t->m[cur].name[j] = nm[j]; j++; }
|
||||
t->m[cur].name[j] = 0;
|
||||
t->m[cur].r = 0.8f; t->m[cur].g = 0.8f; t->m[cur].b = 0.8f;
|
||||
} else if (lp[0]=='K' && lp[1]=='d' && lp[2]==' ' && cur >= 0) {
|
||||
const char *p = lp + 3;
|
||||
t->m[cur].r = obj_atof(&p, le);
|
||||
t->m[cur].g = obj_atof(&p, le);
|
||||
t->m[cur].b = obj_atof(&p, le);
|
||||
}
|
||||
|
||||
lp = (le < buf + len) ? le + 1 : le;
|
||||
}
|
||||
}
|
||||
|
||||
static int mtl_find(const MtlTable *t, const char *name) {
|
||||
for (int i = 0; i < t->n; i++)
|
||||
if (mtl_name_eq(t->m[i].name, name)) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int obj_emit_face(const char *p, const char *end,
|
||||
const float *px, const float *py, const float *pz,
|
||||
const float *nnx, const float *nny, const float *nnz,
|
||||
int nv, int nn, float *out) {
|
||||
int fv[64],fn[64],fvc=0;
|
||||
while(p<end&&*p!='\n'&&*p!='\r'&&fvc<64){
|
||||
while(p<end&&(*p==' '||*p=='\t'))p++;
|
||||
if(p>=end||*p=='\n'||*p=='\r')break;
|
||||
int vi=obj_atoi(&p,end);
|
||||
int ni=0;
|
||||
if(p<end&&*p=='/'){p++;if(p<end&&*p!='/')obj_atoi(&p,end);if(p<end&&*p=='/'){p++;ni=obj_atoi(&p,end);}}
|
||||
if(vi<0)vi=nv+vi+1; vi--;
|
||||
if(ni<0)ni=nn+ni+1; ni--;
|
||||
if(vi>=0&&vi<nv){fv[fvc]=vi;fn[fvc]=ni;fvc++;}
|
||||
}
|
||||
if(fvc<3)return 0;
|
||||
float e1x=px[fv[1]]-px[fv[0]],e1y=py[fv[1]]-py[fv[0]],e1z=pz[fv[1]]-pz[fv[0]];
|
||||
float e2x=px[fv[2]]-px[fv[0]],e2y=py[fv[2]]-py[fv[0]],e2z=pz[fv[2]]-pz[fv[0]];
|
||||
float nx=e1y*e2z-e1z*e2y,ny=e1z*e2x-e1x*e2z,nz=e1x*e2y-e1y*e2x;
|
||||
float nl=obj_sqrtf(nx*nx+ny*ny+nz*nz);if(nl>1e-4f){nx/=nl;ny/=nl;nz/=nl;}
|
||||
/* Store normalized face normal — PS shader computes lighting dynamically */
|
||||
int n=0;
|
||||
for(int t=0;t<fvc-2;t++){
|
||||
int idx[3]={fv[0],fv[t+1],fv[t+2]};
|
||||
int nidx[3]={fn[0],fn[t+1],fn[t+2]};
|
||||
for(int k=0;k<3;k++){
|
||||
float*o=out+n*OBJ_STRIDE;int ii=idx[k];
|
||||
o[0]=px[ii];o[1]=py[ii];o[2]=pz[ii];o[3]=1;
|
||||
/* Use per-vertex normal (vn) if available, else face normal */
|
||||
float vnx,vny,vnz;
|
||||
if(nnx && nidx[k]>=0 && nidx[k]<nn){
|
||||
vnx=nnx[nidx[k]]; vny=nny[nidx[k]]; vnz=nnz[nidx[k]];
|
||||
} else {
|
||||
vnx=nx; vny=ny; vnz=nz;
|
||||
}
|
||||
o[4]=vnx;o[5]=vny;o[6]=vnz;o[7]=0; /* normal for per-pixel Phong */
|
||||
n++;}}
|
||||
return n;
|
||||
}
|
||||
|
||||
/* Stream a file pass: calls line_fn for each complete line.
|
||||
* line_fn(line_start, line_end, user_data). Returns 0 to continue. */
|
||||
typedef int (*obj_line_fn)(const char*, const char*, void*);
|
||||
static void obj_stream_pass(const char *path, obj_line_fn fn, void *ud) {
|
||||
int fd = sceKernelOpen(path, 0, 0);
|
||||
if (fd < 0) return;
|
||||
long ck_ph; unsigned long ck_sz = OBJ_CHUNK + 65536;
|
||||
char *ck = (char*)obj_talloc(ck_sz, &ck_ph);
|
||||
if (!ck) { sceKernelClose(fd); return; }
|
||||
int left = 0;
|
||||
for (;;) {
|
||||
long r = sceKernelRead(fd, ck + left, OBJ_CHUNK);
|
||||
if (r <= 0) {
|
||||
/* Process any remaining data */
|
||||
if (left > 0) { ck[left] = '\n'; fn(ck, ck + left, ud); }
|
||||
break;
|
||||
}
|
||||
long clen = left + r; ck[clen] = 0;
|
||||
const char *cp = ck, *ce = ck + clen;
|
||||
while (cp < ce) {
|
||||
const char *eol = cp; while (eol < ce && *eol != '\n') eol++;
|
||||
if (eol >= ce) {
|
||||
left = (int)(ce - cp); if (left > 65536) left = 65536;
|
||||
for (int i = 0; i < left; i++) ck[i] = cp[i]; break;
|
||||
}
|
||||
fn(cp, eol, ud);
|
||||
cp = eol + 1; left = 0;
|
||||
}
|
||||
if (r < OBJ_CHUNK) break;
|
||||
}
|
||||
sceKernelClose(fd);
|
||||
obj_tfree(ck, ck_ph, ck_sz);
|
||||
}
|
||||
|
||||
/* Pass 1 context: count */
|
||||
typedef struct { int nv; int nn; int nt; int nf_tri; } P1Ctx;
|
||||
static int p1_line(const char *s, const char *e, void *ud) {
|
||||
P1Ctx *c = (P1Ctx*)ud;
|
||||
if (*s == 'v' && s+1 < e && s[1] == ' ') c->nv++;
|
||||
else if (*s == 'v' && s+1 < e && s[1] == 't' && s+2 < e && s[2] == ' ') c->nt++;
|
||||
else if (*s == 'f' && s+1 < e && (s[1]==' '||s[1]=='\t')) {
|
||||
const char *p = s+2; int fvc = 0;
|
||||
while (p<e) { while(p<e&&(*p==' '||*p=='\t'))p++; if(p<e&&*p!='\n'&&*p!='\r'){fvc++;while(p<e&&*p!=' '&&*p!='\t'&&*p!='\n'&&*p!='\r')p++;} else break; }
|
||||
if (fvc >= 3) c->nf_tri += fvc - 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pass 2 context: read positions */
|
||||
typedef struct { float *px, *py, *pz, *nnx, *nny, *nnz, *tu, *tv; int vi, ni, ti, maxv, maxn, maxt; } P2Ctx;
|
||||
static int p2_line(const char *s, const char *e, void *ud) {
|
||||
P2Ctx *c = (P2Ctx*)ud;
|
||||
if (*s == 'v' && s+1 < e && s[1] == 't' && s+2 < e && s[2] == ' ' && c->tu && c->ti < c->maxt) {
|
||||
s += 3;
|
||||
c->tu[c->ti] = obj_atof(&s, e);
|
||||
c->tv[c->ti] = obj_atof(&s, e);
|
||||
c->ti++;
|
||||
} else if (*s == 'v' && s+1 < e && s[1] == ' ' && c->vi < c->maxv) {
|
||||
s += 2;
|
||||
c->px[c->vi] = obj_atof(&s, e);
|
||||
c->py[c->vi] = obj_atof(&s, e);
|
||||
c->pz[c->vi] = obj_atof(&s, e);
|
||||
c->vi++;
|
||||
} else if (*s == 'v' && s+2 < e && s[1] == 'n' && s[2] == ' ' && c->nnx && c->ni < c->maxn) {
|
||||
s += 3;
|
||||
c->nnx[c->ni] = obj_atof(&s, e);
|
||||
c->nny[c->ni] = obj_atof(&s, e);
|
||||
c->nnz[c->ni] = obj_atof(&s, e);
|
||||
c->ni++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pass 3 context: emit faces */
|
||||
typedef struct { const float *px, *py, *pz, *snx, *sny, *snz, *tu, *tv; int nv, nt; float *out; int nout; float cr,cg,cb; const MtlTable *mtl; } P3Ctx;
|
||||
static int p3_emit_smooth_uv(const char *p, const char *end,
|
||||
const float *px, const float *py, const float *pz,
|
||||
const float *snx, const float *sny, const float *snz,
|
||||
const float *tu, const float *tv, int nv, int nt, float *out) {
|
||||
int fv[64], ft[64], fvc = 0;
|
||||
while(p<end&&*p!='\n'&&*p!='\r'&&fvc<64){
|
||||
while(p<end&&(*p==' '||*p=='\t'))p++;
|
||||
if(p>=end||*p=='\n'||*p=='\r')break;
|
||||
int vi=obj_atoi(&p,end);
|
||||
int ti=-1;
|
||||
if(p<end&&*p=='/'){p++;if(p<end&&*p!='/')ti=obj_atoi(&p,end);if(p<end&&*p=='/'){p++;obj_atoi(&p,end);}}
|
||||
if(vi<0)vi=nv+vi+1; vi--;
|
||||
if(ti>0)ti--; else if(ti==0)ti=-1;
|
||||
if(vi>=0&&vi<nv){fv[fvc]=vi;ft[fvc]=ti;fvc++;}
|
||||
}
|
||||
if(fvc<3)return 0;
|
||||
int n=0;
|
||||
for(int t=0;t<fvc-2;t++){
|
||||
int idx[3]={fv[0],fv[t+1],fv[t+2]};
|
||||
int tidx[3]={ft[0],ft[t+1],ft[t+2]};
|
||||
for(int k=0;k<3;k++){
|
||||
float*o=out+n*OBJ_STRIDE; int ii=idx[k]; int ti=tidx[k];
|
||||
o[0]=px[ii];o[1]=py[ii];o[2]=pz[ii];o[3]=1;
|
||||
o[4]=snx[ii];o[5]=sny[ii];o[6]=snz[ii];o[7]=0;
|
||||
o[8]=(ti>=0&&ti<nt)?tu[ti]:0;
|
||||
o[9]=(ti>=0&&ti<nt)?tv[ti]:0;
|
||||
o[10]=0; o[11]=0;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
static int p3_line(const char *s, const char *e, void *ud) {
|
||||
P3Ctx *c = (P3Ctx*)ud;
|
||||
if (s[0]=='u' && s[1]=='s' && s[2]=='e' && s[5]=='l' && s[6]==' ') {
|
||||
/* usemtl name */
|
||||
const char *nm = s + 7;
|
||||
int mi = mtl_find(c->mtl, nm);
|
||||
if (mi >= 0) { c->cr = c->mtl->m[mi].r; c->cg = c->mtl->m[mi].g; c->cb = c->mtl->m[mi].b; }
|
||||
}
|
||||
if (*s == 'f' && s+1 < e && (s[1]==' '||s[1]=='\t')) {
|
||||
int base = c->nout;
|
||||
c->nout += p3_emit_smooth_uv(s+2, e, c->px, c->py, c->pz, c->snx, c->sny, c->snz, c->tu, c->tv, c->nv, c->nt, c->out + c->nout * OBJ_STRIDE);
|
||||
/* UVs stored per-vertex from vt indices */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ================================================================ */
|
||||
/* Progress callback: called between passes with (pass 1-3, detail string, user_data) */
|
||||
typedef void (*obj_progress_fn)(int pass, const char *msg, void *ud);
|
||||
|
||||
|
||||
/* Smooth normal accumulation callback */
|
||||
typedef struct { const float *px,*py,*pz; float *snx,*sny,*snz; int nv; } SNCtx;
|
||||
static int sn_line(const char *s, const char *e, void *ud) {
|
||||
SNCtx *c = (SNCtx*)ud;
|
||||
if (s[0]!='f' || (s[1]!=' ' && s[1]!='\t')) return 0;
|
||||
const char *fp=s+2;
|
||||
int afv[64],afvc=0;
|
||||
while(fp<e&&afvc<64){
|
||||
while(fp<e&&(*fp==' '||*fp=='\t'))fp++;
|
||||
if(fp>=e||*fp=='\n'||*fp=='\r')break;
|
||||
int avi=obj_atoi(&fp,e);
|
||||
if(fp<e&&*fp=='/'){fp++;if(fp<e&&*fp!='/')obj_atoi(&fp,e);if(fp<e&&*fp=='/'){fp++;obj_atoi(&fp,e);}}
|
||||
if(avi<0)avi=c->nv+avi+1; avi--;
|
||||
if(avi>=0&&avi<c->nv)afv[afvc++]=avi;
|
||||
}
|
||||
if(afvc<3)return 0;
|
||||
float e1x=c->px[afv[1]]-c->px[afv[0]],e1y=c->py[afv[1]]-c->py[afv[0]],e1z=c->pz[afv[1]]-c->pz[afv[0]];
|
||||
float e2x=c->px[afv[2]]-c->px[afv[0]],e2y=c->py[afv[2]]-c->py[afv[0]],e2z=c->pz[afv[2]]-c->pz[afv[0]];
|
||||
float fnx=e1y*e2z-e1z*e2y,fny=e1z*e2x-e1x*e2z,fnz=e1x*e2y-e1y*e2x;
|
||||
float fl=obj_sqrtf(fnx*fnx+fny*fny+fnz*fnz);
|
||||
if(fl<1e-6f)return 0; /* skip degenerate faces */
|
||||
fnx/=fl;fny/=fl;fnz/=fl; /* normalize: equal weight per face */
|
||||
for(int i=0;i<afvc;i++){c->snx[afv[i]]+=fnx;c->sny[afv[i]]+=fny;c->snz[afv[i]]+=fnz;}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int obj_load_file(const char *path,
|
||||
void *(*alloc_fn)(unsigned long, unsigned long),
|
||||
ObjMesh *out,
|
||||
obj_progress_fn progress, void *progress_ud) {
|
||||
out->vb_base=0;out->ib_base=0;out->verts=0;
|
||||
out->num_verts=0;out->num_tris=0;out->num_indices=0;
|
||||
out->indexed=0;out->vb_size=0;out->ib_size=0;
|
||||
|
||||
if (progress) progress(0, "Opening file", progress_ud);
|
||||
|
||||
/* ==== PASS 1: count ==== */
|
||||
P1Ctx p1 = {0, 0, 0, 0};
|
||||
obj_stream_pass(path, p1_line, &p1);
|
||||
if (p1.nv == 0 || p1.nf_tri == 0) return -4;
|
||||
|
||||
/* ==== Alloc positions (162MB for 13.5M verts) ==== */
|
||||
unsigned long pos_sz = (unsigned long)p1.nv * 4;
|
||||
long px_ph, py_ph, pz_ph;
|
||||
float *px = (float*)obj_talloc(pos_sz, &px_ph);
|
||||
float *py = (float*)obj_talloc(pos_sz, &py_ph);
|
||||
float *pz = (float*)obj_talloc(pos_sz, &pz_ph);
|
||||
if (!px || !py || !pz) return -5;
|
||||
|
||||
/* Vertex normals (vn) for smooth shading */
|
||||
int nn = p1.nn;
|
||||
unsigned long nrm_sz = nn > 0 ? (unsigned long)nn * 4 : 0;
|
||||
long nnx_ph = -1, nny_ph = -1, nnz_ph = -1;
|
||||
float *nnx = 0, *nny = 0, *nnz = 0;
|
||||
if (nn > 0) {
|
||||
nnx = (float*)obj_talloc(nrm_sz, &nnx_ph);
|
||||
nny = (float*)obj_talloc(nrm_sz, &nny_ph);
|
||||
nnz = (float*)obj_talloc(nrm_sz, &nnz_ph);
|
||||
}
|
||||
|
||||
if (progress) progress(1, "Counting done", progress_ud);
|
||||
|
||||
/* Alloc UVs */
|
||||
int nti = p1.nt > 0 ? p1.nt : 1;
|
||||
long tu_ph = 0, tv_ph = 0;
|
||||
float *tu = (float*)obj_talloc(nti * 4, &tu_ph);
|
||||
float *tv = (float*)obj_talloc(nti * 4, &tv_ph);
|
||||
if (!tu || !tv) { obj_tfree(px,px_ph,pos_sz); obj_tfree(py,py_ph,pos_sz); obj_tfree(pz,pz_ph,pos_sz); return -8; }
|
||||
|
||||
/* ==== PASS 2: read positions + UVs ==== */
|
||||
P2Ctx p2 = { px, py, pz, nnx, nny, nnz, tu, tv, 0, 0, 0, p1.nv, nn, nti };
|
||||
obj_stream_pass(path, p2_line, &p2);
|
||||
|
||||
/* Auto-scale */
|
||||
int vi = p2.vi;
|
||||
float mnx=px[0],mxx=px[0],mny=py[0],mxy=py[0],mnz=pz[0],mxz=pz[0];
|
||||
for(int i=1;i<vi;i++){if(px[i]<mnx)mnx=px[i];if(px[i]>mxx)mxx=px[i];if(py[i]<mny)mny=py[i];if(py[i]>mxy)mxy=py[i];if(pz[i]<mnz)mnz=pz[i];if(pz[i]>mxz)mxz=pz[i];}
|
||||
float cx=(mnx+mxx)*.5f,cy=(mny+mxy)*.5f,cz=(mnz+mxz)*.5f;
|
||||
float rng=mxx-mnx;if(mxy-mny>rng)rng=mxy-mny;if(mxz-mnz>rng)rng=mxz-mnz;
|
||||
float sc=(rng>1e-4f)?1.8f/rng:1;
|
||||
for(int i=0;i<vi;i++){px[i]=(px[i]-cx)*sc;py[i]=(py[i]-cy)*sc;pz[i]=(pz[i]-cz)*sc;}
|
||||
|
||||
/* ==== Alloc VB (2.6GB for 27M tris) ==== */
|
||||
/* Memory: 162MB pos + 2.6GB VB = 2.76GB. Fits! */
|
||||
unsigned long vb_size = OBJ_DATA_OFF + (unsigned long)p1.nf_tri * 3 * OBJ_STRIDE * 4;
|
||||
void *vb = alloc_fn(vb_size, 0x1000);
|
||||
if (!vb) {
|
||||
obj_tfree(tu, tu_ph, nti*4); obj_tfree(tv, tv_ph, nti*4);
|
||||
obj_tfree(px, px_ph, pos_sz);
|
||||
obj_tfree(py, py_ph, pos_sz);
|
||||
obj_tfree(pz, pz_ph, pos_sz);
|
||||
return -6;
|
||||
}
|
||||
float *verts = (float*)((char*)vb + OBJ_DATA_OFF);
|
||||
|
||||
if (progress) progress(2, "Positions loaded", progress_ud);
|
||||
|
||||
/* ==== Compute smooth vertex normals ==== */
|
||||
unsigned long snrm_sz = (unsigned long)vi * 4;
|
||||
long snx_ph=-1, sny_ph=-1, snz_ph=-1;
|
||||
float *snx = (float*)obj_talloc(snrm_sz, &snx_ph);
|
||||
float *sny = (float*)obj_talloc(snrm_sz, &sny_ph);
|
||||
float *snz = (float*)obj_talloc(snrm_sz, &snz_ph);
|
||||
if (!snx||!sny||!snz) return -7;
|
||||
for(int i=0;i<vi;i++){snx[i]=0;sny[i]=0;snz[i]=0;}
|
||||
/* Pass 3a: accumulate via obj_stream_pass */
|
||||
{ SNCtx snctx = { px, py, pz, snx, sny, snz, vi };
|
||||
obj_stream_pass(path, sn_line, &snctx); }
|
||||
for(int i=0;i<vi;i++){
|
||||
float l=obj_sqrtf(snx[i]*snx[i]+sny[i]*sny[i]+snz[i]*snz[i]);
|
||||
if(l>1e-6f){snx[i]/=l;sny[i]/=l;snz[i]/=l;}
|
||||
}
|
||||
|
||||
/* ==== PASS 3: emit faces ==== */
|
||||
/* Load MTL */
|
||||
MtlTable mtl_table;
|
||||
mtl_load(path, &mtl_table);
|
||||
/* Hardcoded Controller materials as fallback */
|
||||
if (mtl_table.n == 0) {
|
||||
struct { const char *n; float r,g,b; } hc[] = {
|
||||
{"Metal bits",0.70f,0.74f,0.74f}, {"Shiny black",0.02f,0.02f,0.02f},
|
||||
{"Blue",0.08f,0.48f,0.94f}, {"Green",0.04f,0.96f,0.16f},
|
||||
{"Red",0.96f,0.10f,0.04f}, {"Yellow",0.96f,0.81f,0.04f},
|
||||
{"Logo black",0.02f,0.02f,0.02f}, {"Shiny white",1.0f,1.0f,1.0f},
|
||||
{"Analogs",1.0f,1.0f,1.0f}, {"Analogs bump",1.0f,1.0f,1.0f},
|
||||
{"Dull white",1.0f,1.0f,1.0f},
|
||||
{"Front buttons transparent",0.8f,0.8f,0.8f},
|
||||
{"Front button inserts",0.02f,0.02f,0.02f}, {0,0,0,0}
|
||||
};
|
||||
for (int hi=0; hc[hi].n && mtl_table.n < MTL_MAX; hi++) {
|
||||
int mi = mtl_table.n++;
|
||||
const char *s = hc[hi].n; int j=0;
|
||||
while (s[j] && j < MTL_NAME_LEN-1) { mtl_table.m[mi].name[j] = s[j]; j++; }
|
||||
mtl_table.m[mi].name[j] = 0;
|
||||
mtl_table.m[mi].r = hc[hi].r;
|
||||
mtl_table.m[mi].g = hc[hi].g;
|
||||
mtl_table.m[mi].b = hc[hi].b;
|
||||
}
|
||||
}
|
||||
P3Ctx p3 = { px, py, pz, snx, sny, snz, tu, tv, vi, nti, verts, 0, 0.8f, 0.8f, 0.8f, &mtl_table };
|
||||
obj_stream_pass(path, p3_line, &p3);
|
||||
|
||||
/* Free positions */
|
||||
obj_tfree(tu, tu_ph, nti*4); obj_tfree(tv, tv_ph, nti*4);
|
||||
obj_tfree(px, px_ph, pos_sz);
|
||||
obj_tfree(py, py_ph, pos_sz);
|
||||
obj_tfree(pz, pz_ph, pos_sz);
|
||||
if (nnx) obj_tfree(nnx, nnx_ph, nrm_sz);
|
||||
if (nny) obj_tfree(nny, nny_ph, nrm_sz);
|
||||
if (nnz) obj_tfree(nnz, nnz_ph, nrm_sz);
|
||||
obj_tfree(snx, snx_ph, snrm_sz);
|
||||
obj_tfree(sny, sny_ph, snrm_sz);
|
||||
obj_tfree(snz, snz_ph, snrm_sz);
|
||||
|
||||
if (progress) progress(3, "Faces done", progress_ud);
|
||||
|
||||
out->vb_base = vb; out->verts = verts; out->ib_base = 0;
|
||||
out->num_verts = p3.nout; out->num_tris = p3.nout / 3;
|
||||
out->num_indices = 0; out->indexed = 0;
|
||||
out->vb_size = vb_size; out->ib_size = 0;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,247 +0,0 @@
|
||||
/* ply_loader.h — ASCII PLY loader
|
||||
* Supports: element vertex N (x,y,z + optional nx,ny,nz)
|
||||
* element face M (property list uchar int vertex_index/vertex_indices)
|
||||
*/
|
||||
|
||||
static int ply_load_file(const char *path, void *(*alloc_fn)(unsigned long,unsigned long),
|
||||
ObjMesh *out, void (*cb)(int,const char*,void*), void *cb_ud) {
|
||||
int fd = sceKernelOpen(path, 0, 0);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
/* Read header (ASCII, line by line) */
|
||||
char hdr[8192]; int hdr_len = 0;
|
||||
int r = sceKernelRead(fd, hdr, sizeof(hdr)-1);
|
||||
if (r <= 0) { sceKernelClose(fd); return -2; }
|
||||
hdr[r] = 0;
|
||||
sceKernelClose(fd);
|
||||
|
||||
/* Parse header */
|
||||
int nv = 0, nf = 0, is_binary = 0;
|
||||
int has_nx = 0; /* has vertex normals */
|
||||
int prop_count = 0; /* properties per vertex */
|
||||
int nx_idx = -1, ny_idx = -1, nz_idx = -1;
|
||||
int in_vertex = 0, in_face = 0;
|
||||
int header_bytes = 0;
|
||||
|
||||
const char *lp = hdr;
|
||||
while (*lp) {
|
||||
const char *le = lp;
|
||||
while (*le && *le != '\n' && *le != '\r') le++;
|
||||
|
||||
int ll = le - lp;
|
||||
if (ll >= 10 && lp[0]=='e' && lp[1]=='n' && lp[2]=='d' && lp[3]=='_') {
|
||||
header_bytes = (le - hdr);
|
||||
while (header_bytes < r && (hdr[header_bytes]=='\n'||hdr[header_bytes]=='\r')) header_bytes++;
|
||||
break;
|
||||
}
|
||||
if (ll >= 6 && lp[0]=='f' && lp[1]=='o' && lp[2]=='r' && lp[3]=='m' && lp[4]=='a' && lp[5]=='t') {
|
||||
if (ll > 11 && lp[7]=='b') is_binary = 1; /* binary_little_endian or binary_big_endian */
|
||||
}
|
||||
if (ll >= 14 && lp[0]=='e' && lp[1]=='l' && lp[2]=='e' && lp[3]=='m') {
|
||||
/* element vertex N or element face N */
|
||||
const char *ep = lp + 8; /* skip "element " */
|
||||
if (ep[0]=='v') {
|
||||
in_vertex = 1; in_face = 0; prop_count = 0;
|
||||
while (ep < le && *ep != ' ') ep++; ep++;
|
||||
nv = obj_atoi(&ep, le);
|
||||
} else if (ep[0]=='f') {
|
||||
in_vertex = 0; in_face = 1;
|
||||
while (ep < le && *ep != ' ') ep++; ep++;
|
||||
nf = obj_atoi(&ep, le);
|
||||
} else { in_vertex = 0; in_face = 0; }
|
||||
}
|
||||
if (ll >= 8 && lp[0]=='p' && lp[1]=='r' && lp[2]=='o' && in_vertex) {
|
||||
/* property float x/y/z/nx/ny/nz */
|
||||
const char *pp = lp;
|
||||
/* Find last word (property name) */
|
||||
const char *name = le - 1;
|
||||
while (name > lp && *name != ' ' && *name != '\t') name--;
|
||||
name++;
|
||||
if (name[0]=='n' && name[1]=='x') { has_nx = 1; nx_idx = prop_count; }
|
||||
if (name[0]=='n' && name[1]=='y') { ny_idx = prop_count; }
|
||||
if (name[0]=='n' && name[1]=='z') { nz_idx = prop_count; }
|
||||
prop_count++;
|
||||
}
|
||||
|
||||
while (*le == '\n' || *le == '\r') le++;
|
||||
lp = le;
|
||||
}
|
||||
|
||||
if (nv == 0 || nf == 0 || is_binary) {
|
||||
/* Binary PLY not supported yet */
|
||||
return is_binary ? -10 : -3;
|
||||
}
|
||||
if (cb) cb(0, "Reading PLY", cb_ud);
|
||||
|
||||
/* Re-read file from after header */
|
||||
fd = sceKernelOpen(path, 0, 0);
|
||||
sceKernelLseek(fd, header_bytes, 0);
|
||||
|
||||
/* Alloc positions */
|
||||
unsigned long pos_sz = (unsigned long)nv * 4;
|
||||
long px_ph, py_ph, pz_ph;
|
||||
float *px = (float*)obj_talloc(pos_sz, &px_ph);
|
||||
float *py = (float*)obj_talloc(pos_sz, &py_ph);
|
||||
float *pz = (float*)obj_talloc(pos_sz, &pz_ph);
|
||||
if (!px||!py||!pz) { sceKernelClose(fd); return -4; }
|
||||
|
||||
/* Read vertices */
|
||||
char line[4096];
|
||||
int vi = 0;
|
||||
/* Simple line reader */
|
||||
char rbuf[65536]; int rbuf_len = 0;
|
||||
int phase = 0; /* 0=verts, 1=faces */
|
||||
int fi = 0;
|
||||
|
||||
/* Count triangles in faces (fan triangulation) */
|
||||
/* First pass: read vertices, count face triangles */
|
||||
int nf_tri = 0;
|
||||
|
||||
/* Read all data into memory (for simplicity with ASCII PLY) */
|
||||
long fsize = sceKernelLseek(fd, 0, 2);
|
||||
sceKernelLseek(fd, header_bytes, 0);
|
||||
long data_size = fsize - header_bytes;
|
||||
long data_ph;
|
||||
char *data = (char*)obj_talloc(data_size + 1, &data_ph);
|
||||
if (!data) { sceKernelClose(fd); return -5; }
|
||||
|
||||
long total = 0;
|
||||
while (total < data_size) {
|
||||
int rd = sceKernelRead(fd, data + total, data_size - total > 65536 ? 65536 : data_size - total);
|
||||
if (rd <= 0) break;
|
||||
total += rd;
|
||||
}
|
||||
data[total] = 0;
|
||||
sceKernelClose(fd);
|
||||
|
||||
if (cb) cb(1, "Parsing vertices", cb_ud);
|
||||
|
||||
/* Parse vertices */
|
||||
const char *dp = data, *de = data + total;
|
||||
for (vi = 0; vi < nv && dp < de; vi++) {
|
||||
float props[16]; int pi = 0;
|
||||
while (dp < de && pi < prop_count && pi < 16) {
|
||||
while (dp < de && (*dp==' '||*dp=='\t')) dp++;
|
||||
if (dp >= de || *dp == '\n' || *dp == '\r') break;
|
||||
props[pi++] = obj_atof(&dp, de);
|
||||
}
|
||||
while (dp < de && *dp != '\n') dp++;
|
||||
if (dp < de) dp++;
|
||||
px[vi] = pi > 0 ? props[0] : 0;
|
||||
py[vi] = pi > 1 ? props[1] : 0;
|
||||
pz[vi] = pi > 2 ? props[2] : 0;
|
||||
}
|
||||
|
||||
/* Count face triangles */
|
||||
const char *face_start = dp;
|
||||
const char *tp = dp;
|
||||
for (fi = 0; fi < nf && tp < de; fi++) {
|
||||
while (tp < de && (*tp==' '||*tp=='\t')) tp++;
|
||||
int fc = 0;
|
||||
if (tp < de) { fc = obj_atoi(&tp, de); }
|
||||
if (fc >= 3) nf_tri += fc - 2;
|
||||
while (tp < de && *tp != '\n') tp++;
|
||||
if (tp < de) tp++;
|
||||
}
|
||||
|
||||
if (cb) cb(2, "Emitting triangles", cb_ud);
|
||||
|
||||
/* Alloc VB */
|
||||
unsigned int nverts_out = nf_tri * 3;
|
||||
unsigned long vb_size = OBJ_DATA_OFF + (unsigned long)nverts_out * OBJ_STRIDE * 4;
|
||||
void *vb = alloc_fn(vb_size, 0x1000);
|
||||
if (!vb) { obj_tfree(data, data_ph, data_size+1); return -6; }
|
||||
|
||||
/* Auto-scale */
|
||||
float minx=1e30f,miny=1e30f,minz=1e30f,maxx=-1e30f,maxy=-1e30f,maxz=-1e30f;
|
||||
for (int i=0;i<vi;i++){
|
||||
if(px[i]<minx)minx=px[i]; if(py[i]<miny)miny=py[i]; if(pz[i]<minz)minz=pz[i];
|
||||
if(px[i]>maxx)maxx=px[i]; if(py[i]>maxy)maxy=py[i]; if(pz[i]>maxz)maxz=pz[i];
|
||||
}
|
||||
float ccx=(minx+maxx)*0.5f, ccy=(miny+maxy)*0.5f, ccz=(minz+maxz)*0.5f;
|
||||
float ddx=maxx-minx, ddy=maxy-miny, ddz=maxz-minz;
|
||||
float maxd=ddx>ddy?ddx:ddy; if(ddz>maxd)maxd=ddz;
|
||||
float sc = maxd > 1e-6f ? 1.8f / maxd : 1.0f;
|
||||
for (int i=0;i<vi;i++){px[i]=(px[i]-ccx)*sc;py[i]=(py[i]-ccy)*sc;pz[i]=(pz[i]-ccz)*sc;}
|
||||
|
||||
/* Compute smooth normals */
|
||||
long snx_ph, sny_ph, snz_ph;
|
||||
float *snx=(float*)obj_talloc(pos_sz,&snx_ph);
|
||||
float *sny=(float*)obj_talloc(pos_sz,&sny_ph);
|
||||
float *snz=(float*)obj_talloc(pos_sz,&snz_ph);
|
||||
if(snx&&sny&&snz){
|
||||
for(int i=0;i<vi;i++){snx[i]=0;sny[i]=0;snz[i]=0;}
|
||||
/* Accumulate face normals */
|
||||
dp = face_start;
|
||||
for (fi=0;fi<nf&&dp<de;fi++){
|
||||
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
|
||||
int fc=obj_atoi(&dp,de);
|
||||
int fv[64]; int fvc=0;
|
||||
for(int i=0;i<fc&&i<64;i++){
|
||||
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
|
||||
fv[fvc++]=obj_atoi(&dp,de);
|
||||
}
|
||||
while(dp<de&&*dp!='\n')dp++;
|
||||
if(dp<de)dp++;
|
||||
if(fvc>=3&&fv[0]>=0&&fv[0]<vi&&fv[1]>=0&&fv[1]<vi&&fv[2]>=0&&fv[2]<vi){
|
||||
float e1x=px[fv[1]]-px[fv[0]],e1y=py[fv[1]]-py[fv[0]],e1z=pz[fv[1]]-pz[fv[0]];
|
||||
float e2x=px[fv[2]]-px[fv[0]],e2y=py[fv[2]]-py[fv[0]],e2z=pz[fv[2]]-pz[fv[0]];
|
||||
float fnx=e1y*e2z-e1z*e2y,fny=e1z*e2x-e1x*e2z,fnz=e1x*e2y-e1y*e2x;
|
||||
float fl=obj_sqrtf(fnx*fnx+fny*fny+fnz*fnz);
|
||||
if(fl>1e-6f){fnx/=fl;fny/=fl;fnz/=fl;}
|
||||
for(int i=0;i<fvc;i++){
|
||||
if(fv[i]>=0&&fv[i]<vi){snx[fv[i]]+=fnx;sny[fv[i]]+=fny;snz[fv[i]]+=fnz;}
|
||||
}
|
||||
}
|
||||
}
|
||||
for(int i=0;i<vi;i++){
|
||||
float l=obj_sqrtf(snx[i]*snx[i]+sny[i]*sny[i]+snz[i]*snz[i]);
|
||||
if(l>1e-6f){snx[i]/=l;sny[i]/=l;snz[i]/=l;}
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit triangles */
|
||||
float *verts = (float*)((char*)vb + OBJ_DATA_OFF);
|
||||
int nout = 0;
|
||||
dp = face_start;
|
||||
for (fi=0;fi<nf&&dp<de;fi++){
|
||||
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
|
||||
int fc=obj_atoi(&dp,de);
|
||||
int fv[64]; int fvc=0;
|
||||
for(int i=0;i<fc&&i<64;i++){
|
||||
while(dp<de&&(*dp==' '||*dp=='\t'))dp++;
|
||||
fv[fvc++]=obj_atoi(&dp,de);
|
||||
}
|
||||
while(dp<de&&*dp!='\n')dp++;
|
||||
if(dp<de)dp++;
|
||||
if(fvc<3)continue;
|
||||
for(int t=0;t<fvc-2;t++){
|
||||
int idx[3]={fv[0],fv[t+1],fv[t+2]};
|
||||
for(int k=0;k<3;k++){
|
||||
int ii=idx[k]; if(ii<0||ii>=vi)continue;
|
||||
float*o=verts+nout*OBJ_STRIDE;
|
||||
o[0]=px[ii];o[1]=py[ii];o[2]=pz[ii];o[3]=1;
|
||||
if(snx){o[4]=snx[ii];o[5]=sny[ii];o[6]=snz[ii];}
|
||||
else{o[4]=0;o[5]=1;o[6]=0;}
|
||||
o[7]=0;
|
||||
o[8]=0.8f;o[9]=0.8f;o[10]=0.8f;o[11]=1.0f;
|
||||
nout++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Cleanup */
|
||||
if(snx){obj_tfree(snx,snx_ph,pos_sz);obj_tfree(sny,sny_ph,pos_sz);obj_tfree(snz,snz_ph,pos_sz);}
|
||||
obj_tfree(px,px_ph,pos_sz);obj_tfree(py,py_ph,pos_sz);obj_tfree(pz,pz_ph,pos_sz);
|
||||
obj_tfree(data,data_ph,data_size+1);
|
||||
|
||||
/* Identity matrix */
|
||||
float *id=(float*)vb;
|
||||
for(int i=0;i<16;i++)id[i]=0; id[0]=1;id[5]=1;id[10]=1;id[15]=1;
|
||||
|
||||
out->vb_base=vb; out->verts=verts; out->ib_base=0;
|
||||
out->num_verts=nout; out->num_tris=nout/3;
|
||||
out->vb_size=vb_size; out->indexed=0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,397 +0,0 @@
|
||||
// ============================================================================
|
||||
// pm4.h — PM4 Command Buffer Builder for PS4 GPU
|
||||
// ============================================================================
|
||||
//
|
||||
// WHAT IS PM4?
|
||||
// PM4 (Packet Manager 4) is AMD's protocol for sending commands to the GPU.
|
||||
// Instead of calling GPU functions directly, you build a command buffer
|
||||
// (a list of PM4 packets) and submit the whole thing at once.
|
||||
//
|
||||
// Think of it like writing a to-do list for the GPU:
|
||||
// "Set this register to X, set that register to Y, now draw 36 triangles"
|
||||
//
|
||||
// HOW PM4 PACKETS WORK:
|
||||
// Every packet starts with a header dword:
|
||||
// Bits [31:30] = 3 (always, means "type 3 packet")
|
||||
// Bits [29:16] = count of body dwords minus 1
|
||||
// Bits [15:8] = opcode (what command)
|
||||
// Bits [7:0] = 0 (predicate, always 0 for us)
|
||||
//
|
||||
// After the header come the body dwords (register offset + values).
|
||||
//
|
||||
// GPU REGISTER TYPES:
|
||||
// The PS4 GPU has thousands of registers, organized in 3 groups:
|
||||
//
|
||||
// CONTEXT registers (0xA000-0xAFFF): Drawing state
|
||||
// Scissors, viewport, depth test, color buffer, blend mode, etc.
|
||||
// Changed between draw calls (e.g., switch depth mode mid-frame).
|
||||
//
|
||||
// SH registers (0x2C00-0x2CFF): Shader state
|
||||
// Shader program address, resource descriptors, user data.
|
||||
// Set before each draw to configure which shaders to use.
|
||||
//
|
||||
// UCONFIG registers (0xC000-0xCFFF): Global GPU config
|
||||
// Primitive type (triangles/lines), instance count.
|
||||
// Rarely changes.
|
||||
//
|
||||
// REGISTER OFFSETS:
|
||||
// All offsets below are relative to their base (already subtracted).
|
||||
// The PM4 helper functions add the base automatically.
|
||||
// Values verified against shadPS4's regs.cpp static_asserts.
|
||||
//
|
||||
// ============================================================================
|
||||
// PM4 command buffer builder for PS4 GFX ring
|
||||
// Register offsets verified against shadPS4 regs.cpp static_asserts
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
// ============================================================================
|
||||
// PM4 packet header helpers
|
||||
// ============================================================================
|
||||
|
||||
// Type 3 packet: [31:30]=11, [29:16]=count-1, [15:8]=opcode, [7:0]=predicate
|
||||
static inline uint32_t pm4_type3(uint32_t opcode, uint32_t count) {
|
||||
return (3u << 30) | (((count)-1) << 16) | ((opcode) << 8);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// PM4 opcodes (from shadPS4 pm4_opcodes.h)
|
||||
// ============================================================================
|
||||
#define PM4_CONTEXT_CONTROL 0x28
|
||||
#define PM4_SET_CONTEXT_REG 0x69
|
||||
#define PM4_SET_SH_REG 0x76
|
||||
#define PM4_SET_UCONFIG_REG 0x79
|
||||
#define PM4_DRAW_INDEX_AUTO 0x2D
|
||||
#define PM4_NUM_INSTANCES 0x2F
|
||||
#define PM4_EVENT_WRITE_EOP 0x47
|
||||
#define PM4_NOP 0x10
|
||||
#define PM4_ACQUIRE_MEM 0x58
|
||||
#define PM4_WRITE_DATA 0x37
|
||||
|
||||
// Register bases
|
||||
#define CTX_REG_BASE 0xA000u
|
||||
#define SH_REG_BASE 0x2C00u
|
||||
#define UCONFIG_REG_BASE 0xC000u
|
||||
|
||||
// ============================================================================
|
||||
// Context register offsets (register - 0xA000)
|
||||
// From shadPS4 regs.cpp static_asserts
|
||||
// ============================================================================
|
||||
#define CTX_DEPTH_RENDER_CONTROL 0x000
|
||||
#define CTX_DEPTH_VIEW 0x002
|
||||
#define CTX_DEPTH_RENDER_OVERRIDE 0x003
|
||||
#define CTX_DEPTH_HTILE_DATA_BASE 0x005
|
||||
#define CTX_SCREEN_SCISSOR 0x00C // 2 dwords
|
||||
#define CTX_DB_Z_INFO 0x010
|
||||
#define CTX_DB_STENCIL_INFO 0x011
|
||||
#define CTX_DB_Z_READ_BASE 0x012
|
||||
#define CTX_DB_STENCIL_READ_BASE 0x013
|
||||
#define CTX_DB_Z_WRITE_BASE 0x014
|
||||
#define CTX_DB_STENCIL_WRITE_BASE 0x015
|
||||
#define CTX_DB_DEPTH_SIZE 0x016
|
||||
#define CTX_DB_DEPTH_SLICE 0x017
|
||||
#define CTX_WINDOW_SCISSOR 0x081 // 2 dwords
|
||||
#define CTX_COLOR_TARGET_MASK 0x08E
|
||||
#define CTX_COLOR_SHADER_MASK 0x08F
|
||||
#define CTX_GENERIC_SCISSOR 0x090 // 2 dwords
|
||||
#define CTX_VIEWPORT_SCISSOR0 0x094 // 2 dwords
|
||||
#define CTX_INDEX_OFFSET 0x102
|
||||
#define CTX_STENCIL_CONTROL 0x10B
|
||||
#define CTX_STENCIL_REF_FRONT 0x10C
|
||||
#define CTX_STENCIL_REF_BACK 0x10D
|
||||
#define CTX_VIEWPORT0 0x10F // 6 dwords (xscale,xoff,yscale,yoff,zscale,zoff)
|
||||
#define CTX_PS_INPUT_CNTL_0 0x191
|
||||
#define CTX_VS_OUTPUT_CONFIG 0x1B1
|
||||
#define CTX_PS_INPUT_ENA 0x1B3
|
||||
#define CTX_PS_INPUT_ADDR 0x1B4
|
||||
#define CTX_NUM_INTERP 0x1B6
|
||||
#define CTX_SHADER_POS_FORMAT 0x1C3
|
||||
#define CTX_Z_EXPORT_FORMAT 0x1C4
|
||||
#define CTX_COLOR_EXPORT_FORMAT 0x1C5
|
||||
#define CTX_BLEND_CONTROL0 0x1E0
|
||||
#define CTX_DEPTH_CONTROL 0x200
|
||||
#define CTX_COLOR_CONTROL 0x202
|
||||
#define CTX_CLIPPER_CONTROL 0x204
|
||||
#define CTX_VIEWPORT_CONTROL 0x206
|
||||
#define CTX_VS_OUTPUT_CONTROL 0x207
|
||||
#define CTX_MODE_CONTROL 0x292
|
||||
#define CTX_INDEX_SIZE 0x29D
|
||||
#define CTX_STAGE_ENABLE 0x2D5
|
||||
#define CTX_AA_CONFIG 0x2F8
|
||||
#define CTX_CB_COLOR0_BASE 0x318
|
||||
#define CTX_CB_COLOR0_PITCH 0x319
|
||||
#define CTX_CB_COLOR0_SLICE 0x31A
|
||||
#define CTX_CB_COLOR0_VIEW 0x31B
|
||||
#define CTX_CB_COLOR0_INFO 0x31C
|
||||
#define CTX_CB_COLOR0_ATTRIB 0x31D
|
||||
|
||||
// SH register offsets (register - 0x2C00)
|
||||
#define SH_PS_PGM_LO 0x08
|
||||
#define SH_PS_PGM_HI 0x09
|
||||
#define SH_PS_PGM_RSRC1 0x0A
|
||||
#define SH_PS_PGM_RSRC2 0x0B
|
||||
#define SH_PS_USER_DATA_0 0x0C
|
||||
#define SH_VS_PGM_LO 0x48
|
||||
#define SH_VS_PGM_HI 0x49
|
||||
#define SH_VS_PGM_RSRC1 0x4A
|
||||
#define SH_VS_PGM_RSRC2 0x4B
|
||||
#define SH_VS_USER_DATA_0 0x4C
|
||||
|
||||
// UCONFIG register offsets (register - 0xC000)
|
||||
#define UCFG_PRIMITIVE_TYPE 0x242
|
||||
#define UCFG_NUM_INSTANCES 0x24D
|
||||
|
||||
// ============================================================================
|
||||
// PM4 command buffer builder (simple linear writer)
|
||||
// ============================================================================
|
||||
struct PM4Builder {
|
||||
uint32_t* buf;
|
||||
uint32_t off;
|
||||
uint32_t cap;
|
||||
};
|
||||
|
||||
static inline void pm4_init(struct PM4Builder* b, uint32_t* buffer, uint32_t capacity_dwords) {
|
||||
b->buf = buffer;
|
||||
b->off = 0;
|
||||
b->cap = capacity_dwords;
|
||||
}
|
||||
|
||||
static inline void pm4_emit(struct PM4Builder* b, uint32_t val) {
|
||||
if (b->off < b->cap) b->buf[b->off++] = val;
|
||||
}
|
||||
|
||||
static inline void pm4_emit_f(struct PM4Builder* b, float val) {
|
||||
union { float f; uint32_t u; } conv;
|
||||
conv.f = val;
|
||||
pm4_emit(b, conv.u);
|
||||
}
|
||||
|
||||
// --- Packet emitters ---
|
||||
|
||||
/* Default hardware-state init — the exact sequence sceGnmDrawInitDefaultHardwareState
|
||||
emits on real PS4. Copied verbatim from shadPS4's gnmdriver.cpp (ClearContextState)
|
||||
and gnmdriver_init.h (base InitSequence, the non-Neo / pre-1.75-SDK variant — correct
|
||||
for a base FAT PS4).
|
||||
|
||||
Real PS4 firmware requires this register-defaults block before any draw: it puts the
|
||||
scan converter, viewport, clip, VGT, and color-buffer registers into a known-good
|
||||
state. Homebrew that skips it leaves those context registers undefined -> GPU hang /
|
||||
black screen on hardware. shadPS4 self-initializes its register tracking, so the
|
||||
omission is invisible there. Emitted once at command-buffer start, before draw state. */
|
||||
static inline void pm4_init_default_hw_state(struct PM4Builder* b) {
|
||||
/* ClearContextState preamble (gnmdriver.cpp ClearStateSequence) */
|
||||
static const uint32_t clear_state[] = {
|
||||
0xc0012800u, 0x80000000u, 0x80000000u, 0xc0001200u, 0u, 0xc0055800u,
|
||||
0x2ec47fc0u, 0xffffffffu, 0u, 0u, 0u, 10u,
|
||||
};
|
||||
for (unsigned i = 0; i < sizeof(clear_state)/sizeof(clear_state[0]); i++)
|
||||
pm4_emit(b, clear_state[i]);
|
||||
|
||||
/* Base InitSequence register defaults (gnmdriver_init.h), minus the 2-dword
|
||||
IT_CLEAR_STATE header which ClearContextState already covers above. */
|
||||
static const uint32_t init_seq[] = {
|
||||
0xc0017600u, 0x216u, 0xffffffffu,
|
||||
0xc0017600u, 0x217u, 0xffffffffu,
|
||||
0xc0017600u, 0x215u, 0u,
|
||||
0xc0016900u, 0x2f9u, 0x2du,
|
||||
0xc0016900u, 0x282u, 8u,
|
||||
0xc0016900u, 0x280u, 0x80008u,
|
||||
0xc0016900u, 0x281u, 0xffff0000u,
|
||||
0xc0016900u, 0x204u, 0u,
|
||||
0xc0016900u, 0x206u, 0x43fu,
|
||||
0xc0016900u, 0x83u, 0xffffu,
|
||||
0xc0016900u, 0x317u, 0x10u,
|
||||
0xc0016900u, 0x2fau, 0x3f800000u,
|
||||
0xc0016900u, 0x2fcu, 0x3f800000u,
|
||||
0xc0016900u, 0x2fbu, 0x3f800000u,
|
||||
0xc0016900u, 0x2fdu, 0x3f800000u,
|
||||
0xc0016900u, 0x202u, 0xcc0010u,
|
||||
0xc0016900u, 0x30eu, 0xffffffffu,
|
||||
0xc0016900u, 0x30fu, 0xffffffffu,
|
||||
0xc0002f00u, 1u,
|
||||
0xc0017600u, 7u, 0x1ffu,
|
||||
0xc0017600u, 0x46u, 0x1ffu,
|
||||
0xc0017600u, 0x87u, 0x1ffu,
|
||||
0xc0017600u, 0xc7u, 0x1ffu,
|
||||
0xc0017600u, 0x107u, 0u,
|
||||
0xc0017600u, 0x147u, 0x1ffu,
|
||||
0xc0016900u, 0x1b1u, 2u,
|
||||
0xc0016900u, 0x101u, 0u,
|
||||
0xc0016900u, 0x100u, 0xffffffffu,
|
||||
0xc0016900u, 0x103u, 0u,
|
||||
0xc0016900u, 0x284u, 0u,
|
||||
0xc0016900u, 0x290u, 0u,
|
||||
0xc0016900u, 0x2aeu, 0u,
|
||||
0xc0016900u, 0x292u, 0u,
|
||||
0xc0016900u, 0x293u, 0x6000000u,
|
||||
0xc0016900u, 0x2f8u, 0u,
|
||||
0xc0016900u, 0x2deu, 0x1e9u,
|
||||
0xc0036900u, 0x295u, 0x100u, 0x100u, 4u,
|
||||
0xc0017900u, 0x200u, 0xe0000000u,
|
||||
};
|
||||
for (unsigned i = 0; i < sizeof(init_seq)/sizeof(init_seq[0]); i++)
|
||||
pm4_emit(b, init_seq[i]);
|
||||
}
|
||||
|
||||
static inline void pm4_context_control(struct PM4Builder* b) {
|
||||
pm4_emit(b, pm4_type3(PM4_CONTEXT_CONTROL, 2));
|
||||
pm4_emit(b, 0x80000000u); // load_control: LOAD_ENABLE
|
||||
pm4_emit(b, 0x80000000u); // shadow_enable: SHADOW_ENABLE
|
||||
}
|
||||
|
||||
static inline void pm4_set_context_reg(struct PM4Builder* b, uint32_t offset, uint32_t value) {
|
||||
pm4_emit(b, pm4_type3(PM4_SET_CONTEXT_REG, 2));
|
||||
pm4_emit(b, offset);
|
||||
pm4_emit(b, value);
|
||||
}
|
||||
|
||||
static inline void pm4_set_context_regs(struct PM4Builder* b, uint32_t offset,
|
||||
const uint32_t* values, uint32_t count) {
|
||||
pm4_emit(b, pm4_type3(PM4_SET_CONTEXT_REG, count + 1));
|
||||
pm4_emit(b, offset);
|
||||
for (uint32_t i = 0; i < count; i++) pm4_emit(b, values[i]);
|
||||
}
|
||||
|
||||
static inline void pm4_set_sh_reg(struct PM4Builder* b, uint32_t offset, uint32_t value) {
|
||||
pm4_emit(b, pm4_type3(PM4_SET_SH_REG, 2));
|
||||
pm4_emit(b, offset);
|
||||
pm4_emit(b, value);
|
||||
}
|
||||
|
||||
static inline void pm4_set_sh_regs(struct PM4Builder* b, uint32_t offset,
|
||||
const uint32_t* values, uint32_t count) {
|
||||
pm4_emit(b, pm4_type3(PM4_SET_SH_REG, count + 1));
|
||||
pm4_emit(b, offset);
|
||||
for (uint32_t i = 0; i < count; i++) pm4_emit(b, values[i]);
|
||||
}
|
||||
|
||||
static inline void pm4_set_uconfig_reg(struct PM4Builder* b, uint32_t offset, uint32_t value) {
|
||||
pm4_emit(b, pm4_type3(PM4_SET_UCONFIG_REG, 2));
|
||||
pm4_emit(b, offset);
|
||||
pm4_emit(b, value);
|
||||
}
|
||||
|
||||
static inline void pm4_draw_index_auto(struct PM4Builder* b, uint32_t index_count) {
|
||||
pm4_emit(b, pm4_type3(PM4_DRAW_INDEX_AUTO, 2));
|
||||
pm4_emit(b, index_count);
|
||||
pm4_emit(b, 2); // draw_initiator: source_select=AUTO
|
||||
}
|
||||
|
||||
static inline void pm4_event_write_eop(struct PM4Builder* b,
|
||||
volatile uint32_t* fence_addr,
|
||||
uint32_t fence_value) {
|
||||
uint64_t addr = (uint64_t)(uintptr_t)fence_addr;
|
||||
pm4_emit(b, pm4_type3(PM4_EVENT_WRITE_EOP, 5));
|
||||
pm4_emit(b, 0x0504u); // CACHE_FLUSH_TS(4), event_index=5
|
||||
pm4_emit(b, (uint32_t)(addr & 0xFFFFFFFFu)); // address_lo
|
||||
pm4_emit(b, (uint32_t)(addr >> 32) | 0x20000000u); // addr_hi | data_sel=1(Data32) | int_sel=0: CPU-polled fence, no per-frame EOP IRQ (we pace on vblank)
|
||||
pm4_emit(b, fence_value); // data_lo
|
||||
pm4_emit(b, 0); // data_hi
|
||||
}
|
||||
|
||||
static inline void pm4_nop(struct PM4Builder* b, uint32_t count) {
|
||||
if (count == 0) return;
|
||||
pm4_emit(b, pm4_type3(PM4_NOP, count));
|
||||
for (uint32_t i = 0; i < count; i++) pm4_emit(b, 0);
|
||||
}
|
||||
|
||||
/* prepareFlip packet — the real PS4 flip handshake.
|
||||
sceGnmSubmitAndFlipCommandBuffers -> the marker-patch pass (gnm fn 0xcb0)
|
||||
requires the DCB to END with a 64-dword TYPE3 NOP whose header is 0xc03e1000
|
||||
(pm4_type3(NOP,63) — our pm4_type3 takes the payload count and subtracts 1,
|
||||
so 63 yields 0xc03e1000) and whose first payload dword is a valid marker.
|
||||
It reads dcb[size_dw - 0x40], so this MUST be the last 64 dwords of the DCB.
|
||||
|
||||
Block layout the patcher reads:
|
||||
+0x00 header 0xc03e1000
|
||||
+0x04 marker
|
||||
+0x08 p0 +0x0c p1 +0x10 p2 +0x14 p3 +0x18 p4
|
||||
+0xe8 is where it writes an EOP (dwords 58..63 — the block's tail)
|
||||
|
||||
EVERY valid marker (0x68750777/78/80/81) makes gnm call
|
||||
sceVideoOutSubmitEopFlip and emit WRITE_DATA(vo_label[bufIdx] = 1). The tag
|
||||
only selects the trailing packet:
|
||||
0x68750777 -> NOP 0xc0391000 (what we used before)
|
||||
0x68750778 -> NOP 0xc0341000 + a 2nd WRITE_DATA (the game's own throttle)
|
||||
0x68750780 -> EOP 0xc0044700, addr=0, DATA_SEL=0, INT_SEL=1 <-- IRQ only
|
||||
0x68750781 -> EOP 0xc0044700, caller addr, DATA_SEL=1, INT_SEL=2
|
||||
|
||||
We use 0x68750780: flip + label write + an interrupt-only EOP.
|
||||
Why: on GFX7 (Liverpool is GFX7) amdgpu sizes its fence array as
|
||||
num_hw_submission*2 = 256*2 = 512, and slots are only freed by
|
||||
amdgpu_fence_process() running off the EOP interrupt
|
||||
(gfx_v7_0_ring_emit_fence_gfx emits INT_SEL(int_sel ? 2 : 0)). Our measured
|
||||
wall is exactly 512 submits with the GPU completing every one — the
|
||||
signature of fences never being processed. Our own EOP is INT_SEL=0 and
|
||||
fires no interrupt; this marker makes the driver's own EOP fire one.
|
||||
p3 = EVENT_TYPE(4 = CACHE_FLUSH_AND_INV_TS), p4 = 0 cache-action bits
|
||||
-> patched dword1 = 4 | (0<<12) | EVENT_INDEX(5)<<8 = 0x504, the same event
|
||||
encoding our own EOP and the game's per-submit EOP both use. */
|
||||
#define PM4_PREPARE_FLIP_TAG 0x68750780u
|
||||
static inline void pm4_prepare_flip(struct PM4Builder* b) {
|
||||
pm4_emit(b, pm4_type3(PM4_NOP, 63)); // +0x00 = 0xc03e1000
|
||||
pm4_emit(b, PM4_PREPARE_FLIP_TAG); // +0x04 marker
|
||||
pm4_emit(b, 0); // +0x08 p0
|
||||
pm4_emit(b, 0); // +0x0c p1
|
||||
pm4_emit(b, 0); // +0x10 p2
|
||||
pm4_emit(b, 4); // +0x14 p3 = EVENT_TYPE
|
||||
pm4_emit(b, 0); // +0x18 p4 = cache actions
|
||||
for (uint32_t i = 0; i < 57; i++) pm4_emit(b, 0); // 64 dwords total
|
||||
}
|
||||
|
||||
/* PM4 DMA_DATA (opcode 0x50): fill GPU memory with a constant value.
|
||||
Unlike CPU memset, this goes through the rasterizer's FillBuffer path which
|
||||
invalidates any cached Vulkan image view for the target memory range. Required
|
||||
when the target is also sampled as a texture elsewhere in the frame (e.g.,
|
||||
shadow_depth: written by shadow CB, sampled by main floor PS). CPU memset
|
||||
would leave stale cached pixels in shadPS4's texture cache, producing ghost
|
||||
silhouettes from previous frames.
|
||||
Layout per PS4 PM4 DMA_DATA packet:
|
||||
header = type3(0x50, 6)
|
||||
dw1 = control: src_sel=2(Data) at [30:29], dst_sel=0(Memory) at [21:20]
|
||||
dw2 = src data (value to fill)
|
||||
dw3 = src_addr_hi (unused for Data source)
|
||||
dw4 = dst_addr_lo
|
||||
dw5 = dst_addr_hi
|
||||
dw6 = command: num_bytes at [20:0] */
|
||||
static inline void pm4_dma_fill(struct PM4Builder* b, void* dst,
|
||||
uint32_t num_bytes, uint32_t fill_value) {
|
||||
uint64_t daddr = (uint64_t)(uintptr_t)dst;
|
||||
pm4_emit(b, pm4_type3(0x50, 6));
|
||||
pm4_emit(b, (2u << 29) | (0u << 20)); /* src_sel=Data, dst_sel=Memory */
|
||||
pm4_emit(b, fill_value); /* fill data */
|
||||
pm4_emit(b, 0); /* src_addr_hi (unused) */
|
||||
pm4_emit(b, (uint32_t)(daddr & 0xFFFFFFFFu)); /* dst_addr_lo */
|
||||
pm4_emit(b, (uint32_t)(daddr >> 32)); /* dst_addr_hi */
|
||||
pm4_emit(b, num_bytes & 0x1FFFFFu); /* command.num_bytes */
|
||||
}
|
||||
#define CTX_POLYGON_CONTROL 0x205 // PA_SU_SC_MODE_CNTL
|
||||
|
||||
/* IT_INDEX_TYPE (opcode 0x2A): set index buffer format */
|
||||
static inline void pm4_index_type(struct PM4Builder* b, uint32_t type) {
|
||||
/* type: 0 = uint16, 1 = uint32 */
|
||||
pm4_emit(b, 0xC0002A00);
|
||||
pm4_emit(b, type);
|
||||
}
|
||||
|
||||
/* IT_DRAW_INDEX_2 (opcode 0x27): indexed draw */
|
||||
static inline void pm4_acquire_mem(struct PM4Builder* b, uint32_t coher_cntl) {
|
||||
/* Flush CB + invalidate TC between render-to-texture phases.
|
||||
GCN Sea Islands PM4 spec: ACQUIRE_MEM(0x58), 6 body dwords */
|
||||
pm4_emit(b, pm4_type3(PM4_ACQUIRE_MEM, 6));
|
||||
pm4_emit(b, coher_cntl); /* CP_COHER_CNTL */
|
||||
pm4_emit(b, 0xFFFFFFFF); /* CP_COHER_SIZE = full range */
|
||||
pm4_emit(b, 0); /* CP_COHER_SIZE_HI */
|
||||
pm4_emit(b, 0); /* CP_COHER_BASE_LO */
|
||||
pm4_emit(b, 0); /* CP_COHER_BASE_HI */
|
||||
pm4_emit(b, 10); /* POLL_INTERVAL */
|
||||
}
|
||||
static inline void pm4_draw_index_2(struct PM4Builder* b, uint32_t max_size,
|
||||
uint64_t index_base, uint32_t index_count) {
|
||||
pm4_emit(b, 0xC0042700);
|
||||
pm4_emit(b, max_size);
|
||||
pm4_emit(b, (uint32_t)(index_base & 0xFFFFFFFF));
|
||||
pm4_emit(b, (uint32_t)(index_base >> 32));
|
||||
pm4_emit(b, index_count);
|
||||
pm4_emit(b, 0); /* draw_initiator: SOURCE_SELECT=DMA (indexed) */
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/* stl_loader.h — Binary + ASCII STL loader
|
||||
* Produces same output as obj_loader: smooth vertex normals, VB at OBJ_DATA_OFF
|
||||
*
|
||||
* Binary STL: 80-byte header, uint32 tri_count, then per-tri:
|
||||
* float[3] normal, float[3] v1, float[3] v2, float[3] v3, uint16 attrib (50 bytes)
|
||||
* ASCII STL: "solid name" ... "facet normal" ... "vertex" ... "endsolid"
|
||||
*/
|
||||
|
||||
#define STL_TRI_SIZE 50
|
||||
|
||||
static int stl_is_binary(const char *path) {
|
||||
int fd = sceKernelOpen(path, 0, 0);
|
||||
if (fd < 0) return -1;
|
||||
char buf[84];
|
||||
int r = sceKernelRead(fd, buf, 84);
|
||||
sceKernelClose(fd);
|
||||
if (r < 84) return 0;
|
||||
/* If header doesn't start with "solid" or has non-ASCII, likely binary */
|
||||
if (buf[0]!='s'||buf[1]!='o'||buf[2]!='l'||buf[3]!='i'||buf[4]!='d') return 1;
|
||||
/* Could be ASCII "solid ..." — check if tri count makes sense */
|
||||
unsigned int ntri = *(unsigned int*)(buf + 80);
|
||||
{ int fd2 = sceKernelOpen(path,0,0); long fsize = fd2>=0 ? sceKernelLseek(fd2, 0, 2) : 0; if(fd2>=0) sceKernelClose(fd2);
|
||||
/* Binary size = 84 + ntri*50 */
|
||||
if (fsize > 0 && fsize == 84 + (long)ntri * 50) return 1; }
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int stl_load_binary(const char *path, void *(*alloc_fn)(unsigned long,unsigned long),
|
||||
ObjMesh *out, void (*cb)(int,const char*,void*), void *cb_ud) {
|
||||
int fd = sceKernelOpen(path, 0, 0);
|
||||
if (fd < 0) return -1;
|
||||
|
||||
/* Read header + count */
|
||||
char hdr[84];
|
||||
sceKernelRead(fd, hdr, 84);
|
||||
unsigned int ntri = *(unsigned int*)(hdr + 80);
|
||||
if (ntri == 0 || ntri > 50000000) { sceKernelClose(fd); return -2; }
|
||||
|
||||
if (cb) cb(0, "Reading STL", cb_ud);
|
||||
|
||||
/* Read all triangle data */
|
||||
unsigned long data_sz = (unsigned long)ntri * STL_TRI_SIZE;
|
||||
long data_ph;
|
||||
char *data = (char*)obj_talloc(data_sz, &data_ph);
|
||||
if (!data) { sceKernelClose(fd); return -3; }
|
||||
|
||||
unsigned long total = 0;
|
||||
while (total < data_sz) {
|
||||
int r = sceKernelRead(fd, data + total, data_sz - total > 65536 ? 65536 : data_sz - total);
|
||||
if (r <= 0) break;
|
||||
total += r;
|
||||
}
|
||||
sceKernelClose(fd);
|
||||
|
||||
/* Extract positions: 3 verts per tri, find unique by hashing */
|
||||
unsigned int nverts = ntri * 3;
|
||||
if (cb) cb(1, "Computing normals", cb_ud);
|
||||
|
||||
/* Alloc VB */
|
||||
unsigned long vb_size = OBJ_DATA_OFF + (unsigned long)nverts * OBJ_STRIDE * 4;
|
||||
void *vb = alloc_fn(vb_size, 0x1000);
|
||||
if (!vb) { obj_tfree(data, data_ph, data_sz); return -4; }
|
||||
|
||||
/* For smooth normals: accumulate face normals per unique position.
|
||||
* STL has no shared vertices, so we weld by position.
|
||||
* Simple approach for <50M tris: just use face normals (flat shading)
|
||||
* and let the smooth normal pass in main handle it if needed.
|
||||
* Actually: emit raw triangles with face normals for now,
|
||||
* smooth normal computation uses position-based welding. */
|
||||
|
||||
float *verts = (float*)((char*)vb + OBJ_DATA_OFF);
|
||||
float minx=1e30f,miny=1e30f,minz=1e30f,maxx=-1e30f,maxy=-1e30f,maxz=-1e30f;
|
||||
|
||||
for (unsigned int t = 0; t < ntri; t++) {
|
||||
const float *tri = (const float*)(data + t * STL_TRI_SIZE);
|
||||
float nx = tri[0], ny = tri[1], nz = tri[2]; /* face normal from file */
|
||||
float nl = obj_sqrtf(nx*nx+ny*ny+nz*nz);
|
||||
if (nl < 1e-6f) {
|
||||
/* Compute from vertices */
|
||||
float e1x=tri[7]-tri[4],e1y=tri[8]-tri[5],e1z=tri[9]-tri[6];
|
||||
float e2x=tri[10]-tri[4],e2y=tri[11]-tri[5],e2z=tri[12]-tri[6];
|
||||
nx=e1y*e2z-e1z*e2y; ny=e1z*e2x-e1x*e2z; nz=e1x*e2y-e1y*e2x;
|
||||
nl=obj_sqrtf(nx*nx+ny*ny+nz*nz);
|
||||
}
|
||||
if (nl>1e-6f){nx/=nl;ny/=nl;nz/=nl;}
|
||||
|
||||
for (int v = 0; v < 3; v++) {
|
||||
float px = tri[3+v*3], py = tri[4+v*3], pz = tri[5+v*3];
|
||||
if(px<minx)minx=px; if(py<miny)miny=py; if(pz<minz)minz=pz;
|
||||
if(px>maxx)maxx=px; if(py>maxy)maxy=py; if(pz>maxz)maxz=pz;
|
||||
int idx = (t*3+v) * OBJ_STRIDE;
|
||||
verts[idx]=px; verts[idx+1]=py; verts[idx+2]=pz; verts[idx+3]=1;
|
||||
verts[idx+4]=nx; verts[idx+5]=ny; verts[idx+6]=nz; verts[idx+7]=0;
|
||||
verts[idx+8]=0.8f; verts[idx+9]=0.8f; verts[idx+10]=0.8f; verts[idx+11]=1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
obj_tfree(data, data_ph, data_sz);
|
||||
|
||||
/* Auto-scale to ±0.9 */
|
||||
if (cb) cb(2, "Scaling", cb_ud);
|
||||
float cx=(minx+maxx)*0.5f, cy=(miny+maxy)*0.5f, cz=(minz+maxz)*0.5f;
|
||||
float dx=maxx-minx, dy=maxy-miny, dz=maxz-minz;
|
||||
float maxd=dx>dy?dx:dy; if(dz>maxd)maxd=dz;
|
||||
float sc = maxd > 1e-6f ? 1.8f / maxd : 1.0f;
|
||||
for (unsigned int i = 0; i < nverts; i++) {
|
||||
int idx = i * OBJ_STRIDE;
|
||||
verts[idx] = (verts[idx]-cx)*sc;
|
||||
verts[idx+1] = (verts[idx+1]-cy)*sc;
|
||||
verts[idx+2] = (verts[idx+2]-cz)*sc;
|
||||
}
|
||||
|
||||
/* Copy BG/MVP header from identity template */
|
||||
float *id = (float*)vb;
|
||||
for(int i=0;i<16;i++) id[i]=0; id[0]=1;id[5]=1;id[10]=1;id[15]=1;
|
||||
|
||||
out->vb_base = vb; out->verts = verts; out->ib_base = 0;
|
||||
out->num_verts = nverts; out->num_tris = ntri;
|
||||
out->vb_size = vb_size; out->indexed = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user