207 lines
12 KiB
C
207 lines
12 KiB
C
#include <windows.h>
|
|
#include <d3d11.h>
|
|
#include <d3dcompiler.h>
|
|
#include <math.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
static ID3D11Device* g_dev; static ID3D11DeviceContext* g_ctx;
|
|
static IDXGISwapChain* g_sc; static ID3D11RenderTargetView* g_rtv;
|
|
static ID3D11VertexShader* g_vs; static ID3D11PixelShader* g_ps;
|
|
static ID3D11InputLayout* g_il; static ID3D11Buffer* g_cb;
|
|
|
|
static float cam_dist = 5.0f, cam_yaw = 0.5f, cam_pitch = 0.3f;
|
|
static int drag = 0, last_mx, last_my;
|
|
static int key_w=0,key_s=0,key_a=0,key_d=0,key_q=0,key_e=0;
|
|
|
|
typedef struct { float p[3], n[3], u[2], c[4]; } V3D;
|
|
|
|
static V3D* obj_verts = NULL;
|
|
static uint32_t* obj_idx = NULL;
|
|
static int obj_vcount = 0, obj_icount = 0;
|
|
static ID3D11Buffer* obj_vb = NULL;
|
|
static ID3D11Buffer* obj_ib = NULL;
|
|
|
|
static int LoadOBJ(const char* path) {
|
|
FILE* f = fopen(path, "r");
|
|
if (!f) return -1;
|
|
int max_v = 16384, max_f = 32768;
|
|
float* vpos = (float*)malloc(max_v * 3 * sizeof(float));
|
|
int vc = 0;
|
|
int* fidx = (int*)malloc(max_f * 3 * sizeof(int));
|
|
int fc = 0;
|
|
char line[256];
|
|
while (fgets(line, sizeof(line), f)) {
|
|
if (line[0] == 'v' && line[1] == ' ') {
|
|
float x, y, z;
|
|
if (sscanf(line + 2, "%f %f %f", &x, &y, &z) == 3 && vc < max_v) {
|
|
vpos[vc*3]=x; vpos[vc*3+1]=y; vpos[vc*3+2]=z; vc++;
|
|
}
|
|
} else if (line[0] == 'f' && line[1] == ' ') {
|
|
int v0, v1, v2;
|
|
char* p = line + 2;
|
|
v0 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
|
|
v1 = atoi(p) - 1; while (*p && *p != ' ') p++; p++;
|
|
v2 = atoi(p) - 1;
|
|
if (v0 >= 0 && v1 >= 0 && v2 >= 0 && fc < max_f) {
|
|
fidx[fc*3]=v0; fidx[fc*3+1]=v1; fidx[fc*3+2]=v2; fc++;
|
|
}
|
|
}
|
|
}
|
|
fclose(f);
|
|
if (vc == 0 || fc == 0) { free(vpos); free(fidx); return -1; }
|
|
|
|
obj_vcount = fc * 3;
|
|
obj_icount = fc * 3;
|
|
obj_verts = (V3D*)calloc(obj_vcount, sizeof(V3D));
|
|
obj_idx = (uint32_t*)malloc(obj_icount * sizeof(uint32_t));
|
|
|
|
for (int i = 0; i < fc; i++) {
|
|
for (int j = 0; j < 3; j++) {
|
|
int vi = fidx[i*3+j];
|
|
int oi = i*3+j;
|
|
if (vi < vc) {
|
|
obj_verts[oi].p[0] = vpos[vi*3];
|
|
obj_verts[oi].p[1] = vpos[vi*3+1];
|
|
obj_verts[oi].p[2] = vpos[vi*3+2];
|
|
}
|
|
obj_verts[oi].c[0] = 0.6f; obj_verts[oi].c[1] = 0.7f; obj_verts[oi].c[2] = 0.8f; obj_verts[oi].c[3] = 1;
|
|
obj_idx[oi] = oi;
|
|
}
|
|
}
|
|
for (int i = 0; i < fc; i++) {
|
|
float* p0 = obj_verts[i*3].p;
|
|
float* p1 = obj_verts[i*3+1].p;
|
|
float* p2 = obj_verts[i*3+2].p;
|
|
float e1[3]={p1[0]-p0[0],p1[1]-p0[1],p1[2]-p0[2]};
|
|
float e2[3]={p2[0]-p0[0],p2[1]-p0[1],p2[2]-p0[2]};
|
|
float nx=e1[1]*e2[2]-e1[2]*e2[1], ny=e1[2]*e2[0]-e1[0]*e2[2], nz=e1[0]*e2[1]-e1[1]*e2[0];
|
|
float nl=sqrtf(nx*nx+ny*ny+nz*nz);
|
|
if(nl>0.0001f){nx/=nl;ny/=nl;nz/=nl;}
|
|
for(int j=0;j<3;j++){obj_verts[i*3+j].n[0]=nx;obj_verts[i*3+j].n[1]=ny;obj_verts[i*3+j].n[2]=nz;}
|
|
}
|
|
|
|
D3D11_BUFFER_DESC bd; D3D11_SUBRESOURCE_DATA sd;
|
|
memset(&bd,0,sizeof(bd));memset(&sd,0,sizeof(sd));
|
|
bd.ByteWidth=sizeof(V3D)*obj_vcount;bd.Usage=D3D11_USAGE_DEFAULT;bd.BindFlags=D3D11_BIND_VERTEX_BUFFER;
|
|
sd.pSysMem=obj_verts;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&obj_vb);
|
|
bd.ByteWidth=sizeof(uint32_t)*obj_icount;bd.BindFlags=D3D11_BIND_INDEX_BUFFER;
|
|
sd.pSysMem=obj_idx;g_dev->lpVtbl->CreateBuffer(g_dev,&bd,&sd,&obj_ib);
|
|
|
|
free(vpos); free(fidx); free(obj_verts); free(obj_idx);
|
|
obj_verts = NULL; obj_idx = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static void Init(HWND h) {
|
|
HRESULT hr;
|
|
DXGI_SWAP_CHAIN_DESC sd;memset(&sd,0,sizeof(sd));sd.BufferCount=2;sd.BufferDesc.Format=DXGI_FORMAT_R8G8B8A8_UNORM;sd.BufferDesc.RefreshRate.Numerator=60;sd.BufferUsage=DXGI_USAGE_RENDER_TARGET_OUTPUT;sd.OutputWindow=h;sd.SampleDesc.Count=1;sd.Windowed=TRUE;sd.SwapEffect=DXGI_SWAP_EFFECT_DISCARD;
|
|
D3D_FEATURE_LEVEL fl=D3D_FEATURE_LEVEL_11_0,gt;hr=D3D11CreateDeviceAndSwapChain(NULL,D3D_DRIVER_TYPE_HARDWARE,NULL,0,&fl,1,D3D11_SDK_VERSION,&sd,&g_sc,&g_dev,>,&g_ctx);if(FAILED(hr))return;
|
|
ID3D11Texture2D*bb=NULL;hr=g_sc->lpVtbl->GetBuffer(g_sc,0,&IID_ID3D11Texture2D,(void**)&bb);if(FAILED(hr)||!bb)return;hr=g_dev->lpVtbl->CreateRenderTargetView(g_dev,(ID3D11Resource*)bb,NULL,&g_rtv);bb->lpVtbl->Release(bb);if(FAILED(hr))return;
|
|
const char*vs="cbuffer CB:register(b0){float4x4 WVP;};struct I{float3 p:POSITION;float3 n:NORMAL;float2 u:TEXCOORD;float4 c:COLOR;};struct O{float4 p:SV_POSITION;float3 n:NORMAL;float4 c:COLOR;};O main(I i){O o;o.p=mul(float4(i.p,1),WVP);o.n=i.n;o.c=i.c;return o;}";
|
|
const char*ps="struct I{float3 n:NORMAL;float4 c:COLOR;};float4 main(float4 p:SV_POSITION,I i):SV_Target{float l=normalize(float3(0.3,1,0.5));float d=max(dot(normalize(i.n),l),0.2);return float4(i.c.rgb*d,1);}";
|
|
ID3DBlob*b1,*b2,*e;D3DCompile(vs,(UINT)strlen(vs),NULL,NULL,NULL,"main","vs_5_0",0,0,&b1,&e);if(e){e->lpVtbl->Release(e);e=NULL;}D3DCompile(ps,(UINT)strlen(ps),NULL,NULL,NULL,"main","ps_5_0",0,0,&b2,&e);if(e){e->lpVtbl->Release(e);e=NULL;}if(!b1||!b2)return;
|
|
g_dev->lpVtbl->CreateVertexShader(g_dev,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),NULL,&g_vs);g_dev->lpVtbl->CreatePixelShader(g_dev,b2->lpVtbl->GetBufferPointer(b2),b2->lpVtbl->GetBufferSize(b2),NULL,&g_ps);
|
|
D3D11_INPUT_ELEMENT_DESC el[4];memset(el,0,sizeof(el));el[0].SemanticName="POSITION";el[0].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[0].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[1].SemanticName="NORMAL";el[1].AlignedByteOffset=12;el[1].Format=DXGI_FORMAT_R32G32B32_FLOAT;el[1].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[2].SemanticName="TEXCOORD";el[2].AlignedByteOffset=24;el[2].Format=DXGI_FORMAT_R32G32_FLOAT;el[2].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;el[3].SemanticName="COLOR";el[3].AlignedByteOffset=32;el[3].Format=DXGI_FORMAT_R32G32B32A32_FLOAT;el[3].InputSlotClass=D3D11_INPUT_PER_VERTEX_DATA;
|
|
g_dev->lpVtbl->CreateInputLayout(g_dev,el,4,b1->lpVtbl->GetBufferPointer(b1),b1->lpVtbl->GetBufferSize(b1),&g_il);b1->lpVtbl->Release(b1);b2->lpVtbl->Release(b2);
|
|
D3D11_BUFFER_DESC cbd;memset(&cbd,0,sizeof(cbd));cbd.ByteWidth=256;cbd.Usage=D3D11_USAGE_DEFAULT;cbd.BindFlags=D3D11_BIND_CONSTANT_BUFFER;g_dev->lpVtbl->CreateBuffer(g_dev,&cbd,NULL,&g_cb);
|
|
}
|
|
|
|
static void mat4_perspective(float* m, float fov, float asp, float zn, float zf) {
|
|
memset(m, 0, 64);
|
|
float f = 1.0f / tanf(fov / 2);
|
|
m[0] = f / asp; m[5] = -f;
|
|
m[10] = zf / (zf - zn); m[11] = -zn * zf / (zf - zn);
|
|
m[14] = 1; m[15] = 0;
|
|
}
|
|
|
|
static void mat4_lookat(float* m, float ex, float ey, float ez, float tx, float ty, float tz) {
|
|
float fx=tx-ex, fy=ty-ey, fz=tz-ez;
|
|
float fl=sqrtf(fx*fx+fy*fy+fz*fz);fx/=fl;fy/=fl;fz/=fl;
|
|
float rx=fy*0-fz*1,ry=fz*0-fx*0,rz=fx*1-fy*0;
|
|
float rl=sqrtf(rx*rx+ry*ry+rz*rz);rx/=rl;ry/=rl;rz/=rl;
|
|
float ux=ry*fz-rz*fy,uy=rz*fx-rx*fz,uz=rx*fy-ry*fx;
|
|
memset(m, 0, 64);
|
|
m[0]=rx*1;m[1]=ux*1;m[2]=fx*1;
|
|
m[4]=ry*1;m[5]=uy*1;m[6]=fy*1;
|
|
m[8]=rz*1;m[9]=uz*1;m[10]=fz*1;
|
|
m[12]=-(rx*ex+ry*ey+rz*ez);m[13]=-(ux*ex+uy*ey+uz*ez);m[14]=-(fx*ex+fy*ey+fz*ez);m[15]=1;
|
|
}
|
|
|
|
static void mat4_mul(float* o, float* a, float* b) {
|
|
float t[16];for(int i=0;i<4;i++)for(int j=0;j<4;j++){t[i*4+j]=0;for(int k=0;k<4;k++)t[i*4+j]+=a[i*4+k]*b[k*4+j];}
|
|
memcpy(o,t,64);
|
|
}
|
|
|
|
static void Frame(void) {
|
|
float dt = 0.016f, rspd = 2.0f * dt, mspeed = 3.0f * dt;
|
|
if (key_a) cam_yaw -= rspd; if (key_d) cam_yaw += rspd;
|
|
if (key_q) cam_pitch -= rspd; if (key_e) cam_pitch += rspd;
|
|
if (key_w) cam_dist -= mspeed; if (key_s) cam_dist += mspeed;
|
|
if (cam_dist < 1.0f) cam_dist = 1.0f; if (cam_dist > 20.0f) cam_dist = 20.0f;
|
|
if (cam_pitch > 1.5f) cam_pitch = 1.5f; if (cam_pitch < -1.5f) cam_pitch = -1.5f;
|
|
|
|
float ex = cam_dist * sinf(cam_yaw) * cosf(cam_pitch);
|
|
float ey = cam_dist * sinf(cam_pitch);
|
|
float ez = cam_dist * cosf(cam_yaw) * cosf(cam_pitch);
|
|
|
|
float view[16], proj[16], vp[16], tmp[16];
|
|
mat4_lookat(view, ex, ey, ez, 0, 0, 0);
|
|
mat4_perspective(proj, 0.8f, 1280.0f/720.0f, 0.1f, 100.0f);
|
|
mat4_mul(tmp, view, proj);
|
|
float vp_t[16]; for(int i=0;i<4;i++)for(int j=0;j<4;j++) vp_t[i*4+j]=tmp[j*4+i];
|
|
|
|
float cl[4]={.15f,.18f,.22f,1};
|
|
g_ctx->lpVtbl->OMSetRenderTargets(g_ctx,1,&g_rtv,NULL);g_ctx->lpVtbl->ClearRenderTargetView(g_ctx,g_rtv,cl);
|
|
D3D11_VIEWPORT vp2;memset(&vp2,0,sizeof(vp2));vp2.Width=1280;vp2.Height=720;vp2.MaxDepth=1;g_ctx->lpVtbl->RSSetViewports(g_ctx,1,&vp2);
|
|
D3D11_RASTERIZER_DESC rd;memset(&rd,0,sizeof(rd));rd.FillMode=D3D11_FILL_SOLID;rd.CullMode=D3D11_CULL_NONE;ID3D11RasterizerState*rs;g_dev->lpVtbl->CreateRasterizerState(g_dev,&rd,&rs);g_ctx->lpVtbl->RSSetState(g_ctx,rs);
|
|
D3D11_DEPTH_STENCIL_DESC ds;memset(&ds,0,sizeof(ds));ds.DepthEnable=FALSE;ID3D11DepthStencilState*dss;g_dev->lpVtbl->CreateDepthStencilState(g_dev,&ds,&dss);g_ctx->lpVtbl->OMSetDepthStencilState(g_ctx,dss,0);
|
|
g_ctx->lpVtbl->IASetPrimitiveTopology(g_ctx,D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);g_ctx->lpVtbl->VSSetShader(g_ctx,g_vs,NULL,0);g_ctx->lpVtbl->PSSetShader(g_ctx,g_ps,NULL,0);g_ctx->lpVtbl->IASetInputLayout(g_ctx,g_il);g_ctx->lpVtbl->VSSetConstantBuffers(g_ctx,0,1,&g_cb);g_ctx->lpVtbl->UpdateSubresource(g_ctx,(ID3D11Resource*)g_cb,0,NULL,vp_t,0,0);
|
|
|
|
if (obj_vb && obj_ib) {
|
|
UINT stride = sizeof(V3D), off = 0;
|
|
g_ctx->lpVtbl->IASetVertexBuffers(g_ctx, 0, 1, &obj_vb, &stride, &off);
|
|
g_ctx->lpVtbl->IASetIndexBuffer(g_ctx, obj_ib, DXGI_FORMAT_R32_UINT, 0);
|
|
g_ctx->lpVtbl->DrawIndexed(g_ctx, obj_icount, 0, 0);
|
|
}
|
|
|
|
if(rs)rs->lpVtbl->Release(rs);if(dss)dss->lpVtbl->Release(dss);
|
|
g_sc->lpVtbl->Present(g_sc, 1, 0);
|
|
}
|
|
|
|
static LRESULT CALLBACK WP(HWND h,UINT m,WPARAM w,LPARAM l){
|
|
switch(m){
|
|
case WM_LBUTTONDOWN:drag=1;last_mx=(short)LOWORD(l);last_my=(short)HIWORD(l);SetCapture(h);break;
|
|
case WM_LBUTTONUP:drag=0;ReleaseCapture();break;
|
|
case WM_MOUSEMOVE:if(drag){int mx=(short)LOWORD(l),my=(short)HIWORD(l);cam_yaw+=(mx-last_mx)*0.005f;cam_pitch+=(my-last_my)*0.005f;if(cam_pitch>1.5f)cam_pitch=1.5f;if(cam_pitch<-1.5f)cam_pitch=-1.5f;last_mx=mx;last_my=my;}break;
|
|
case WM_MOUSEWHEEL:cam_dist-=(short)HIWORD(w)/120.0f;if(cam_dist<1)cam_dist=1;if(cam_dist>20)cam_dist=20;break;
|
|
case WM_KEYDOWN:switch(w){case 'A':key_a=1;break;case 'D':key_d=1;break;case 'W':key_w=1;break;case 'S':key_s=1;break;case 'Q':key_q=1;break;case 'E':key_e=1;break;case VK_ESCAPE:PostQuitMessage(0);break;}break;
|
|
case WM_KEYUP:switch(w){case 'A':key_a=0;break;case 'D':key_d=0;break;case 'W':key_w=0;break;case 'S':key_s=0;break;case 'Q':key_q=0;break;case 'E':key_e=0;break;}break;
|
|
case WM_DESTROY:PostQuitMessage(0);return 0;
|
|
}return DefWindowProcW(h,m,w,l);
|
|
}
|
|
|
|
int WINAPI WinMain(HINSTANCE hI,HINSTANCE,LPSTR nC,int nS){
|
|
(void)nS;
|
|
WNDCLASSEXW wc;memset(&wc,0,sizeof(wc));wc.cbSize=sizeof(wc);wc.style=CS_CLASSDC;wc.lpfnWndProc=WP;wc.hInstance=hI;wc.lpszClassName=L"obj_viewer";
|
|
RegisterClassExW(&wc);
|
|
HWND hwnd=CreateWindowW(L"obj_viewer",L"OBJ Viewer - Mouse drag orbit, WASD move, Scroll zoom",WS_OVERLAPPEDWINDOW,100,100,1280,720,NULL,NULL,hI,NULL);
|
|
if(!hwnd)return -1;ShowWindow(hwnd,SW_SHOW);UpdateWindow(hwnd);Init(hwnd);
|
|
|
|
char path[MAX_PATH];
|
|
GetModuleFileNameA(NULL, path, MAX_PATH);
|
|
char* sl = strrchr(path, '\\');
|
|
if (sl) { *(sl+1) = '\0'; strcat(path, "Assets\\cube.obj"); }
|
|
else strcpy(path, "Assets\\cube.obj");
|
|
LoadOBJ(path);
|
|
|
|
MSG msg;
|
|
while(1){while(PeekMessage(&msg,NULL,0,0,PM_REMOVE)){TranslateMessage(&msg);DispatchMessage(&msg);if(msg.message==WM_QUIT)goto done;}Frame();}
|
|
done:
|
|
if(obj_vb)obj_vb->lpVtbl->Release(obj_vb);if(obj_ib)obj_ib->lpVtbl->Release(obj_ib);
|
|
if(g_cb)g_cb->lpVtbl->Release(g_cb);if(g_il)g_il->lpVtbl->Release(g_il);if(g_vs)g_vs->lpVtbl->Release(g_vs);if(g_ps)g_ps->lpVtbl->Release(g_ps);if(g_rtv)g_rtv->lpVtbl->Release(g_rtv);if(g_sc)g_sc->lpVtbl->Release(g_sc);if(g_ctx)g_ctx->lpVtbl->Release(g_ctx);if(g_dev)g_dev->lpVtbl->Release(g_dev);
|
|
return 0;
|
|
}
|