|
@@ -1,463 +1,463 @@
|
|
|
unit GLUtils;
|
|
|
interface
|
|
|
uses
|
|
|
- MemoryBuffer, Mat4, GLTypes,
|
|
|
- BrowserConsole, WebGL, JS,
|
|
|
- Types, SysUtils;
|
|
|
+ MemoryBuffer, Mat4, GLTypes,
|
|
|
+ BrowserConsole, WebGL, JS,
|
|
|
+ Types, SysUtils;
|
|
|
|
|
|
type
|
|
|
- TShader = class
|
|
|
- public
|
|
|
- constructor Create (context: TJSWebGLRenderingContext; vertexShaderSource, fragmentShaderSource: string);
|
|
|
- procedure Compile;
|
|
|
- procedure Link;
|
|
|
- procedure Use;
|
|
|
+ TShader = class
|
|
|
+ public
|
|
|
+ constructor Create (context: TJSWebGLRenderingContext; vertexShaderSource, fragmentShaderSource: string);
|
|
|
+ procedure Compile;
|
|
|
+ procedure Link;
|
|
|
+ procedure Use;
|
|
|
|
|
|
- function GetAttribLocation (name: string): GLint;
|
|
|
- procedure BindAttribLocation (index: GLuint; name: string);
|
|
|
+ function GetAttribLocation (name: string): GLint;
|
|
|
+ procedure BindAttribLocation (index: GLuint; name: string);
|
|
|
|
|
|
- procedure SetUniformMat4 (name: string; value: TMat4);
|
|
|
- procedure SetUniformVec3 (name: string; value: TVec3);
|
|
|
- procedure SetUniformFloat (name: string; value: GLfloat);
|
|
|
+ procedure SetUniformMat4 (name: string; value: TMat4);
|
|
|
+ procedure SetUniformVec3 (name: string; value: TVec3);
|
|
|
+ procedure SetUniformFloat (name: string; value: GLfloat);
|
|
|
|
|
|
- private
|
|
|
- gl: TJSWebGLRenderingContext;
|
|
|
- vertexShader: TJSWebGLShader;
|
|
|
- fragmentShader: TJSWebGLShader;
|
|
|
- programID: TJSWebGLProgram;
|
|
|
+ private
|
|
|
+ gl: TJSWebGLRenderingContext;
|
|
|
+ vertexShader: TJSWebGLShader;
|
|
|
+ fragmentShader: TJSWebGLShader;
|
|
|
+ programID: TJSWebGLProgram;
|
|
|
|
|
|
- function GetUniformLocation (name: string): TJSWebGLUniformLocation;
|
|
|
- function CreateShader (theType: GLenum; source: string): TJSWebGLShader;
|
|
|
- end;
|
|
|
+ function GetUniformLocation (name: string): TJSWebGLUniformLocation;
|
|
|
+ function CreateShader (theType: GLenum; source: string): TJSWebGLShader;
|
|
|
+ end;
|
|
|
|
|
|
|
|
|
type
|
|
|
- TModelData = record
|
|
|
- verticies: TJSFloat32Array; // GLfloat
|
|
|
+ TModelData = record
|
|
|
+ verticies: TJSFloat32Array; // GLfloat
|
|
|
|
|
|
- // NOTE: it's not clear if WebGL supports GLuint
|
|
|
- // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/drawElements
|
|
|
+ // NOTE: it's not clear if WebGL supports GLuint
|
|
|
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebGLRenderingContext/drawElements
|
|
|
|
|
|
- indicies: TJSUint16Array; // GLushort
|
|
|
- floatsPerVertex: integer;
|
|
|
- end;
|
|
|
+ indicies: TJSUint16Array; // GLushort
|
|
|
+ floatsPerVertex: integer;
|
|
|
+ end;
|
|
|
|
|
|
const
|
|
|
- kModelVertexFloats = 3 + 2 + 3;
|
|
|
+ kModelVertexFloats = 3 + 2 + 3;
|
|
|
type
|
|
|
- TModelVertex = record
|
|
|
- pos: TVec3;
|
|
|
- texCoord: TVec2;
|
|
|
- normal: TVec3;
|
|
|
- end;
|
|
|
+ TModelVertex = record
|
|
|
+ pos: TVec3;
|
|
|
+ texCoord: TVec2;
|
|
|
+ normal: TVec3;
|
|
|
+ end;
|
|
|
|
|
|
procedure ModelVertexAddToBuffer(vertex: TModelVertex; buffer: TMemoryBuffer);
|
|
|
-procedure ModelVertexAddToArray (vertex: TModelVertex; list: TJSArray);
|
|
|
+procedure ModelVertexAddToArray (vertex: TModelVertex; list: TJSArray);
|
|
|
|
|
|
type
|
|
|
- TModel = class
|
|
|
- public
|
|
|
- constructor Create(context: TJSWebGLRenderingContext; modelData: TModelData); overload;
|
|
|
- procedure Draw;
|
|
|
- private
|
|
|
- gl: TJSWebGLRenderingContext;
|
|
|
- data: TModelData;
|
|
|
- vertexBuffer: TJSWebGLBuffer;
|
|
|
- indexBuffer: TJSWebGLBuffer;
|
|
|
- //elementCount: integer;
|
|
|
-
|
|
|
- procedure EnableAttributes;
|
|
|
- procedure Load;
|
|
|
- end;
|
|
|
+ TModel = class
|
|
|
+ public
|
|
|
+ constructor Create(context: TJSWebGLRenderingContext; modelData: TModelData); overload;
|
|
|
+ procedure Draw;
|
|
|
+ private
|
|
|
+ gl: TJSWebGLRenderingContext;
|
|
|
+ data: TModelData;
|
|
|
+ vertexBuffer: TJSWebGLBuffer;
|
|
|
+ indexBuffer: TJSWebGLBuffer;
|
|
|
+ //elementCount: integer;
|
|
|
+
|
|
|
+ procedure EnableAttributes;
|
|
|
+ procedure Load;
|
|
|
+ end;
|
|
|
|
|
|
function LoadOBJFile (text: TJSString): TModelData;
|
|
|
|
|
|
-function GLSizeof(glType: NativeInt): integer;
|
|
|
-procedure GLFatal (gl: TJSWebGLRenderingContext; messageString: string = 'Fatal OpenGL error');
|
|
|
+function GLSizeof(glType: NativeInt): integer;
|
|
|
+procedure GLFatal (gl: TJSWebGLRenderingContext; messageString: string = 'Fatal OpenGL error');
|
|
|
|
|
|
implementation
|
|
|
|
|
|
{=============================================}
|
|
|
-{@! ___Utilities___ }
|
|
|
+{@! ___Utilities___ }
|
|
|
{=============================================}
|
|
|
|
|
|
procedure Fatal (messageString: string); overload;
|
|
|
begin
|
|
|
- writeln('*** FATAL: ', messageString);
|
|
|
- raise Exception.Create('FATAL');
|
|
|
+ writeln('*** FATAL: ', messageString);
|
|
|
+ raise Exception.Create('FATAL');
|
|
|
end;
|
|
|
|
|
|
// TODO: toll free bridge to FPC strings
|
|
|
{procedure Fatal (messageString: TJSString); overload;
|
|
|
begin
|
|
|
- writeln('*** FATAL: ', messageString);
|
|
|
- raise Exception.Create('FATAL');
|
|
|
+ writeln('*** FATAL: ', messageString);
|
|
|
+ raise Exception.Create('FATAL');
|
|
|
end;}
|
|
|
|
|
|
-procedure GLFatal (gl: TJSWebGLRenderingContext; messageString: string = 'Fatal OpenGL error');
|
|
|
+procedure GLFatal (gl: TJSWebGLRenderingContext; messageString: string = 'Fatal OpenGL error');
|
|
|
var
|
|
|
- error: integer;
|
|
|
+ error: integer;
|
|
|
begin
|
|
|
- error := gl.getError();
|
|
|
- if error <> TJSWebGLRenderingContext.NO_ERROR then
|
|
|
- begin
|
|
|
- // TODO: case doesn't work?
|
|
|
- case error of
|
|
|
- TJSWebGLRenderingContext.INVALID_VALUE:
|
|
|
- messageString := messageString+' (GL_INVALID_VALUE)';
|
|
|
- TJSWebGLRenderingContext.INVALID_OPERATION:
|
|
|
- messageString := messageString+' (GL_INVALID_OPERATION)';
|
|
|
- TJSWebGLRenderingContext.INVALID_ENUM:
|
|
|
- messageString := messageString+' (GL_INVALID_ENUM)';
|
|
|
- otherwise
|
|
|
- messageString := messageString+' '+IntToStr(error);
|
|
|
- end;
|
|
|
- Fatal(messageString);
|
|
|
- end;
|
|
|
+ error := gl.getError();
|
|
|
+ if error <> TJSWebGLRenderingContext.NO_ERROR then
|
|
|
+ begin
|
|
|
+ // TODO: case doesn't work?
|
|
|
+ case error of
|
|
|
+ TJSWebGLRenderingContext.INVALID_VALUE:
|
|
|
+ messageString := messageString+' (GL_INVALID_VALUE)';
|
|
|
+ TJSWebGLRenderingContext.INVALID_OPERATION:
|
|
|
+ messageString := messageString+' (GL_INVALID_OPERATION)';
|
|
|
+ TJSWebGLRenderingContext.INVALID_ENUM:
|
|
|
+ messageString := messageString+' (GL_INVALID_ENUM)';
|
|
|
+ otherwise
|
|
|
+ messageString := messageString+' '+IntToStr(error);
|
|
|
+ end;
|
|
|
+ Fatal(messageString);
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
-function GLSizeof(glType: NativeInt): integer;
|
|
|
+function GLSizeof(glType: NativeInt): integer;
|
|
|
begin
|
|
|
- case glType of
|
|
|
- TJSWebGLRenderingContext.UNSIGNED_BYTE, TJSWebGLRenderingContext.BYTE:
|
|
|
- result := 1;
|
|
|
- TJSWebGLRenderingContext.SHORT, TJSWebGLRenderingContext.UNSIGNED_SHORT:
|
|
|
- result := 2;
|
|
|
- TJSWebGLRenderingContext.INT, TJSWebGLRenderingContext.UNSIGNED_INT:
|
|
|
- result := 4;
|
|
|
- TJSWebGLRenderingContext.FLOAT:
|
|
|
- result := 4;
|
|
|
- otherwise
|
|
|
- Fatal('GLSizeof type is invalid.');
|
|
|
- end;
|
|
|
+ case glType of
|
|
|
+ TJSWebGLRenderingContext.UNSIGNED_BYTE, TJSWebGLRenderingContext.BYTE:
|
|
|
+ result := 1;
|
|
|
+ TJSWebGLRenderingContext.SHORT, TJSWebGLRenderingContext.UNSIGNED_SHORT:
|
|
|
+ result := 2;
|
|
|
+ TJSWebGLRenderingContext.INT, TJSWebGLRenderingContext.UNSIGNED_INT:
|
|
|
+ result := 4;
|
|
|
+ TJSWebGLRenderingContext.FLOAT:
|
|
|
+ result := 4;
|
|
|
+ otherwise
|
|
|
+ Fatal('GLSizeof type is invalid.');
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
{=============================================}
|
|
|
-{@! ___Model___ }
|
|
|
+{@! ___Model___ }
|
|
|
{=============================================}
|
|
|
|
|
|
procedure ModelVertexAddToBuffer(vertex: TModelVertex; buffer: TMemoryBuffer);
|
|
|
begin
|
|
|
- buffer.AddFloats(kModelVertexFloats, [
|
|
|
- vertex.pos.x, vertex.pos.y, vertex.pos.z,
|
|
|
- vertex.texCoord.x, vertex.texCoord.y,
|
|
|
- vertex.normal.x, vertex.normal.y, vertex.normal.z
|
|
|
- ]);
|
|
|
+ buffer.AddFloats(kModelVertexFloats, [
|
|
|
+ vertex.pos.x, vertex.pos.y, vertex.pos.z,
|
|
|
+ vertex.texCoord.x, vertex.texCoord.y,
|
|
|
+ vertex.normal.x, vertex.normal.y, vertex.normal.z
|
|
|
+ ]);
|
|
|
end;
|
|
|
|
|
|
-procedure ModelVertexAddToArray (vertex: TModelVertex; list: TJSArray);
|
|
|
+procedure ModelVertexAddToArray (vertex: TModelVertex; list: TJSArray);
|
|
|
begin
|
|
|
- list.push(vertex.pos.x);
|
|
|
- list.push(vertex.pos.y);
|
|
|
- list.push(vertex.pos.z);
|
|
|
- list.push(vertex.texCoord.x);
|
|
|
- list.push(vertex.texCoord.y);
|
|
|
- list.push(vertex.normal.x);
|
|
|
- list.push(vertex.normal.y);
|
|
|
- list.push(vertex.normal.z);
|
|
|
+ list.push(vertex.pos.x);
|
|
|
+ list.push(vertex.pos.y);
|
|
|
+ list.push(vertex.pos.z);
|
|
|
+ list.push(vertex.texCoord.x);
|
|
|
+ list.push(vertex.texCoord.y);
|
|
|
+ list.push(vertex.normal.x);
|
|
|
+ list.push(vertex.normal.y);
|
|
|
+ list.push(vertex.normal.z);
|
|
|
end;
|
|
|
|
|
|
constructor TModel.Create(context: TJSWebGLRenderingContext; modelData: TModelData);
|
|
|
begin
|
|
|
- gl := context;
|
|
|
- data := modelData;
|
|
|
- Load;
|
|
|
+ gl := context;
|
|
|
+ data := modelData;
|
|
|
+ Load;
|
|
|
end;
|
|
|
|
|
|
procedure TModel.Draw;
|
|
|
begin
|
|
|
- gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
|
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
|
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
|
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
|
|
|
|
- EnableAttributes;
|
|
|
- gl.drawElements(gl.TRIANGLES, data.indicies.length, gl.UNSIGNED_SHORT, 0);
|
|
|
+ EnableAttributes;
|
|
|
+ gl.drawElements(gl.TRIANGLES, data.indicies.length, gl.UNSIGNED_SHORT, 0);
|
|
|
|
|
|
- gl.bindBuffer(gl.ARRAY_BUFFER, nil);
|
|
|
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, nil);
|
|
|
+ gl.bindBuffer(gl.ARRAY_BUFFER, nil);
|
|
|
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, nil);
|
|
|
end;
|
|
|
|
|
|
procedure TModel.EnableAttributes;
|
|
|
var
|
|
|
- offset: integer;
|
|
|
- stride: integer;
|
|
|
+ offset: integer;
|
|
|
+ stride: integer;
|
|
|
begin
|
|
|
|
|
|
- // NOTE: we don't have VAO's yet so we need to enable vertex attributes for shader
|
|
|
- // before every draw call (unless the array buffer hasn't changed between calls)
|
|
|
- offset := 0;
|
|
|
- stride := data.floatsPerVertex * GLSizeof(TJSWebGLRenderingContext.FLOAT);
|
|
|
-
|
|
|
- // position
|
|
|
- gl.enableVertexAttribArray(0);
|
|
|
- gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, offset);
|
|
|
- offset += GLSizeof(TJSWebGLRenderingContext.FLOAT) * 3;
|
|
|
-
|
|
|
- // texture
|
|
|
- gl.enableVertexAttribArray(1);
|
|
|
- gl.vertexAttribPointer(1, 2, gl.FLOAT, false, stride, offset);
|
|
|
- offset += GLSizeof(TJSWebGLRenderingContext.FLOAT) * 2;
|
|
|
-
|
|
|
- // normal
|
|
|
- gl.enableVertexAttribArray(2);
|
|
|
- gl.vertexAttribPointer(2, 3, gl.FLOAT, false, stride, offset);
|
|
|
- offset += GLSizeof(TJSWebGLRenderingContext.FLOAT) * 3;
|
|
|
+ // NOTE: we don't have VAO's yet so we need to enable vertex attributes for shader
|
|
|
+ // before every draw call (unless the array buffer hasn't changed between calls)
|
|
|
+ offset := 0;
|
|
|
+ stride := data.floatsPerVertex * GLSizeof(TJSWebGLRenderingContext.FLOAT);
|
|
|
+
|
|
|
+ // position
|
|
|
+ gl.enableVertexAttribArray(0);
|
|
|
+ gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, offset);
|
|
|
+ offset += GLSizeof(TJSWebGLRenderingContext.FLOAT) * 3;
|
|
|
+
|
|
|
+ // texture
|
|
|
+ gl.enableVertexAttribArray(1);
|
|
|
+ gl.vertexAttribPointer(1, 2, gl.FLOAT, false, stride, offset);
|
|
|
+ offset += GLSizeof(TJSWebGLRenderingContext.FLOAT) * 2;
|
|
|
+
|
|
|
+ // normal
|
|
|
+ gl.enableVertexAttribArray(2);
|
|
|
+ gl.vertexAttribPointer(2, 3, gl.FLOAT, false, stride, offset);
|
|
|
+ offset += GLSizeof(TJSWebGLRenderingContext.FLOAT) * 3;
|
|
|
end;
|
|
|
|
|
|
procedure TModel.Load;
|
|
|
begin
|
|
|
- indexBuffer := gl.createBuffer;
|
|
|
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
|
- gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.indicies, gl.STATIC_DRAW);
|
|
|
- gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, nil);
|
|
|
+ indexBuffer := gl.createBuffer;
|
|
|
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
|
|
|
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, data.indicies, gl.STATIC_DRAW);
|
|
|
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, nil);
|
|
|
|
|
|
- vertexBuffer := gl.createBuffer;
|
|
|
- gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
|
+ vertexBuffer := gl.createBuffer;
|
|
|
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
|
|
|
gl.bufferData(gl.ARRAY_BUFFER, data.verticies, gl.STATIC_DRAW);
|
|
|
- gl.bindBuffer(gl.ARRAY_BUFFER, nil);
|
|
|
+ gl.bindBuffer(gl.ARRAY_BUFFER, nil);
|
|
|
end;
|
|
|
|
|
|
type
|
|
|
- TOBJVertex = record
|
|
|
- position: TVec3;
|
|
|
- textureIndex: integer;
|
|
|
- normalIndex: integer;
|
|
|
- end;
|
|
|
+ TOBJVertex = record
|
|
|
+ position: TVec3;
|
|
|
+ textureIndex: integer;
|
|
|
+ normalIndex: integer;
|
|
|
+ end;
|
|
|
|
|
|
function ProcessFace (verticies: TJSArray; indices: TJSArray; face: TStringDynArray): TOBJVertex;
|
|
|
var
|
|
|
- index: integer;
|
|
|
- vertex: TOBJVertex;
|
|
|
+ index: integer;
|
|
|
+ vertex: TOBJVertex;
|
|
|
begin
|
|
|
- index := StrToInt(face[0]) - 1;
|
|
|
+ index := StrToInt(face[0]) - 1;
|
|
|
|
|
|
- vertex := TOBJVertex(verticies[index]);
|
|
|
+ vertex := TOBJVertex(verticies[index]);
|
|
|
|
|
|
- // NOTE: see TOBJData
|
|
|
- // index can't exceed GLushort
|
|
|
- if index > 65536 then
|
|
|
- Fatal('overflowed indices array');
|
|
|
+ // NOTE: see TOBJData
|
|
|
+ // index can't exceed GLushort
|
|
|
+ if index > 65536 then
|
|
|
+ Fatal('overflowed indices array');
|
|
|
|
|
|
- if face[1] <> '' then
|
|
|
- vertex.textureIndex := StrToInt(face[1]) - 1
|
|
|
- else
|
|
|
- vertex.textureIndex := -1;
|
|
|
+ if face[1] <> '' then
|
|
|
+ vertex.textureIndex := StrToInt(face[1]) - 1
|
|
|
+ else
|
|
|
+ vertex.textureIndex := -1;
|
|
|
|
|
|
- if face[2] <> '' then
|
|
|
- vertex.normalIndex := StrToInt(face[2]) - 1
|
|
|
- else
|
|
|
- vertex.normalIndex := -1;
|
|
|
+ if face[2] <> '' then
|
|
|
+ vertex.normalIndex := StrToInt(face[2]) - 1
|
|
|
+ else
|
|
|
+ vertex.normalIndex := -1;
|
|
|
|
|
|
- indices.push(index);
|
|
|
-
|
|
|
- verticies[index] := vertex;
|
|
|
+ indices.push(index);
|
|
|
|
|
|
- result := vertex;
|
|
|
+ verticies[index] := vertex;
|
|
|
+
|
|
|
+ result := vertex;
|
|
|
end;
|
|
|
|
|
|
function LoadOBJFile (text: TJSString): TModelData;
|
|
|
const
|
|
|
- kLineEnding = #10;
|
|
|
- kSpace = ' '; // what code is space?
|
|
|
+ kLineEnding = #10;
|
|
|
+ kSpace = ' '; // what code is space?
|
|
|
var
|
|
|
- lines: TStringDynArray;
|
|
|
- parts: TStringDynArray;
|
|
|
- indices: TJSArray;
|
|
|
- positions: TJSArray;
|
|
|
- normals: TJSArray;
|
|
|
- textures: TJSArray;
|
|
|
- verticies: TJSArray;
|
|
|
- mesh: TJSFloat32Array;
|
|
|
-
|
|
|
- i: integer;
|
|
|
- line: TJSString;
|
|
|
- vertex: TOBJVertex;
|
|
|
- vertexIndex: integer;
|
|
|
- data: TModelData;
|
|
|
-
|
|
|
- pos: TVec3;
|
|
|
- texCoord: TVec2;
|
|
|
- normal: TVec3;
|
|
|
+ lines: TStringDynArray;
|
|
|
+ parts: TStringDynArray;
|
|
|
+ indices: TJSArray;
|
|
|
+ positions: TJSArray;
|
|
|
+ normals: TJSArray;
|
|
|
+ textures: TJSArray;
|
|
|
+ verticies: TJSArray;
|
|
|
+ mesh: TJSFloat32Array;
|
|
|
+
|
|
|
+ i: integer;
|
|
|
+ line: TJSString;
|
|
|
+ vertex: TOBJVertex;
|
|
|
+ vertexIndex: integer;
|
|
|
+ data: TModelData;
|
|
|
+
|
|
|
+ pos: TVec3;
|
|
|
+ texCoord: TVec2;
|
|
|
+ normal: TVec3;
|
|
|
begin
|
|
|
- positions := TJSArray.new;
|
|
|
- normals := TJSArray.new;
|
|
|
- textures := TJSArray.new;
|
|
|
- indices := TJSArray.new;
|
|
|
- verticies := TJSArray.new;
|
|
|
-
|
|
|
- lines := text.split(kLineEnding);
|
|
|
-
|
|
|
- for i := 0 to high(lines) do
|
|
|
- begin
|
|
|
- line := TJSString(lines[i]);
|
|
|
- parts := line.split(kSpace);
|
|
|
-
|
|
|
- if line.startsWith('v ') then
|
|
|
- begin
|
|
|
- pos := V3(StrToFloat(parts[1]), StrToFloat(parts[2]), StrToFloat(parts[3]));
|
|
|
- positions.push(pos);
|
|
|
-
|
|
|
- // add new vertex
|
|
|
- vertex.position := pos;
|
|
|
- vertex.textureIndex := -1;
|
|
|
- vertex.normalIndex := -1;
|
|
|
- verticies.push(pos);
|
|
|
- end
|
|
|
- else if line.startsWith('vn ') then
|
|
|
- begin
|
|
|
- normals.push(V3(StrToFloat(parts[1]), StrToFloat(parts[2]), StrToFloat(parts[3])));
|
|
|
- end
|
|
|
- else if line.startsWith('vt ') then
|
|
|
- begin
|
|
|
- textures.push(V2(StrToFloat(parts[1]), 1 - StrToFloat(parts[2])));
|
|
|
- end
|
|
|
- else if line.startsWith('f ') then
|
|
|
- begin
|
|
|
- ProcessFace(verticies, indices, TJSString(parts[1]).split('/'));
|
|
|
- ProcessFace(verticies, indices, TJSString(parts[2]).split('/'));
|
|
|
- ProcessFace(verticies, indices, TJSString(parts[3]).split('/'));
|
|
|
- end;
|
|
|
- end;
|
|
|
-
|
|
|
- // vec3 (position) + vec2 (texCoord) + vec3 (normal)
|
|
|
- data.floatsPerVertex := kModelVertexFloats;
|
|
|
-
|
|
|
- mesh := TJSFloat32Array.New(data.floatsPerVertex * verticies.length);
|
|
|
-
|
|
|
- for i := 0 to verticies.length - 1 do
|
|
|
- begin
|
|
|
- vertex := TOBJVertex(verticies[i]);
|
|
|
-
|
|
|
- vertexIndex := i * data.floatsPerVertex;
|
|
|
-
|
|
|
- // position
|
|
|
- pos := TVec3(positions[i]);
|
|
|
- mesh[vertexIndex + 0] := pos.x;
|
|
|
- mesh[vertexIndex + 1] := pos.y;
|
|
|
- mesh[vertexIndex + 2] := pos.z;
|
|
|
-
|
|
|
- // texture
|
|
|
- if vertex.textureIndex <> -1 then
|
|
|
- begin
|
|
|
- texCoord := TVec2(textures[vertex.textureIndex]);
|
|
|
- mesh[vertexIndex + 3] := texCoord.x;
|
|
|
- mesh[vertexIndex + 4] := texCoord.y;
|
|
|
- end
|
|
|
- else
|
|
|
- begin
|
|
|
- mesh[vertexIndex + 3] := 0;
|
|
|
- mesh[vertexIndex + 4] := 0;
|
|
|
- end;
|
|
|
-
|
|
|
- // normal
|
|
|
- if vertex.normalIndex <> -1 then
|
|
|
- begin
|
|
|
- normal := TVec3(normals[vertex.normalIndex]);
|
|
|
- mesh[vertexIndex + 5] := normal.x;
|
|
|
- mesh[vertexIndex + 6] := normal.y;
|
|
|
- mesh[vertexIndex + 7] := normal.z;
|
|
|
- end;
|
|
|
- end;
|
|
|
-
|
|
|
- //writeln('floats: ', mesh.length);
|
|
|
- //writeln('positions:', positions.length);
|
|
|
- //writeln('indices:', indices.length);
|
|
|
-
|
|
|
- data.verticies := mesh;
|
|
|
- data.indicies := TJSUint16Array.New(TJSObject(indices));
|
|
|
-
|
|
|
- result := data;
|
|
|
+ positions := TJSArray.new;
|
|
|
+ normals := TJSArray.new;
|
|
|
+ textures := TJSArray.new;
|
|
|
+ indices := TJSArray.new;
|
|
|
+ verticies := TJSArray.new;
|
|
|
+
|
|
|
+ lines := text.split(kLineEnding);
|
|
|
+
|
|
|
+ for i := 0 to high(lines) do
|
|
|
+ begin
|
|
|
+ line := TJSString(lines[i]);
|
|
|
+ parts := line.split(kSpace);
|
|
|
+
|
|
|
+ if line.startsWith('v ') then
|
|
|
+ begin
|
|
|
+ pos := V3(StrToFloat(parts[1]), StrToFloat(parts[2]), StrToFloat(parts[3]));
|
|
|
+ positions.push(pos);
|
|
|
+
|
|
|
+ // add new vertex
|
|
|
+ vertex.position := pos;
|
|
|
+ vertex.textureIndex := -1;
|
|
|
+ vertex.normalIndex := -1;
|
|
|
+ verticies.push(vertex);
|
|
|
+ end
|
|
|
+ else if line.startsWith('vn ') then
|
|
|
+ begin
|
|
|
+ normals.push(V3(StrToFloat(parts[1]), StrToFloat(parts[2]), StrToFloat(parts[3])));
|
|
|
+ end
|
|
|
+ else if line.startsWith('vt ') then
|
|
|
+ begin
|
|
|
+ textures.push(V2(StrToFloat(parts[1]), 1 - StrToFloat(parts[2])));
|
|
|
+ end
|
|
|
+ else if line.startsWith('f ') then
|
|
|
+ begin
|
|
|
+ ProcessFace(verticies, indices, TJSString(parts[1]).split('/'));
|
|
|
+ ProcessFace(verticies, indices, TJSString(parts[2]).split('/'));
|
|
|
+ ProcessFace(verticies, indices, TJSString(parts[3]).split('/'));
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ // vec3 (position) + vec2 (texCoord) + vec3 (normal)
|
|
|
+ data.floatsPerVertex := kModelVertexFloats;
|
|
|
+
|
|
|
+ mesh := TJSFloat32Array.New(data.floatsPerVertex * verticies.length);
|
|
|
+
|
|
|
+ for i := 0 to verticies.length - 1 do
|
|
|
+ begin
|
|
|
+ vertex := TOBJVertex(verticies[i]);
|
|
|
+
|
|
|
+ vertexIndex := i * data.floatsPerVertex;
|
|
|
+
|
|
|
+ // position
|
|
|
+ pos := TVec3(positions[i]);
|
|
|
+ mesh[vertexIndex + 0] := pos.x;
|
|
|
+ mesh[vertexIndex + 1] := pos.y;
|
|
|
+ mesh[vertexIndex + 2] := pos.z;
|
|
|
+
|
|
|
+ // texture
|
|
|
+ if vertex.textureIndex <> -1 then
|
|
|
+ begin
|
|
|
+ texCoord := TVec2(textures[vertex.textureIndex]);
|
|
|
+ mesh[vertexIndex + 3] := texCoord.x;
|
|
|
+ mesh[vertexIndex + 4] := texCoord.y;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ mesh[vertexIndex + 3] := 0;
|
|
|
+ mesh[vertexIndex + 4] := 0;
|
|
|
+ end;
|
|
|
+
|
|
|
+ // normal
|
|
|
+ if vertex.normalIndex <> -1 then
|
|
|
+ begin
|
|
|
+ normal := TVec3(normals[vertex.normalIndex]);
|
|
|
+ mesh[vertexIndex + 5] := normal.x;
|
|
|
+ mesh[vertexIndex + 6] := normal.y;
|
|
|
+ mesh[vertexIndex + 7] := normal.z;
|
|
|
+ end;
|
|
|
+ end;
|
|
|
+
|
|
|
+ //writeln('floats: ', mesh.length);
|
|
|
+ //writeln('positions:', positions.length);
|
|
|
+ //writeln('indices:', indices.length);
|
|
|
+
|
|
|
+ data.verticies := mesh;
|
|
|
+ data.indicies := TJSUint16Array.New(TJSObject(indices));
|
|
|
+
|
|
|
+ result := data;
|
|
|
end;
|
|
|
|
|
|
{=============================================}
|
|
|
-{@! ___Shader___ }
|
|
|
+{@! ___Shader___ }
|
|
|
{=============================================}
|
|
|
function TShader.GetUniformLocation (name: string): TJSWebGLUniformLocation;
|
|
|
begin
|
|
|
- // TODO: cache these. how do we use dictionarys from JS in Pascal?
|
|
|
- result := gl.getUniformLocation(programID, name);
|
|
|
- GLFatal(gl, 'gl.getUniformLocation');
|
|
|
+ // TODO: cache these. how do we use dictionarys from JS in Pascal?
|
|
|
+ result := gl.getUniformLocation(programID, name);
|
|
|
+ GLFatal(gl, 'gl.getUniformLocation');
|
|
|
end;
|
|
|
|
|
|
procedure TShader.SetUniformFloat (name: string; value: GLfloat);
|
|
|
begin
|
|
|
- gl.uniform1f(GetUniformLocation(name), value);
|
|
|
- GLFatal(gl, 'gl.uniform1f');
|
|
|
+ gl.uniform1f(GetUniformLocation(name), value);
|
|
|
+ GLFatal(gl, 'gl.uniform1f');
|
|
|
end;
|
|
|
|
|
|
procedure TShader.SetUniformVec3 (name: string; value: TVec3);
|
|
|
begin
|
|
|
- //gl.uniform3fv(GetUniformLocation(name), ToFloats(value));
|
|
|
- gl.uniform3f(GetUniformLocation(name), value.x, value.y, value.z);
|
|
|
- GLFatal(gl, 'gl.uniform3fv');
|
|
|
+ //gl.uniform3fv(GetUniformLocation(name), ToFloats(value));
|
|
|
+ gl.uniform3f(GetUniformLocation(name), value.x, value.y, value.z);
|
|
|
+ GLFatal(gl, 'gl.uniform3fv');
|
|
|
end;
|
|
|
|
|
|
procedure TShader.SetUniformMat4 (name: string; value: TMat4);
|
|
|
var
|
|
|
- list: TJSFloat32List;
|
|
|
+ list: TJSFloat32List;
|
|
|
begin
|
|
|
- // TODO: fix mat4 to use flat arrays
|
|
|
- list := TJSFloat32List(value.CopyList);
|
|
|
- gl.uniformMatrix4fv(GetUniformLocation(name), false, list);
|
|
|
- GLFatal(gl, 'gl.uniformMatrix4fv');
|
|
|
+ // TODO: fix mat4 to use flat arrays
|
|
|
+ list := TJSFloat32List(value.CopyList);
|
|
|
+ gl.uniformMatrix4fv(GetUniformLocation(name), false, list);
|
|
|
+ GLFatal(gl, 'gl.uniformMatrix4fv');
|
|
|
end;
|
|
|
|
|
|
function TShader.GetAttribLocation (name: string): GLint;
|
|
|
begin
|
|
|
- result := gl.getAttribLocation(programID, name);
|
|
|
+ result := gl.getAttribLocation(programID, name);
|
|
|
end;
|
|
|
|
|
|
procedure TShader.BindAttribLocation (index: GLuint; name: string);
|
|
|
begin
|
|
|
- gl.bindAttribLocation(programID, index, name);
|
|
|
- //GLFatal('glBindAttribLocation '+IntToStr(index)+':'+name);
|
|
|
+ gl.bindAttribLocation(programID, index, name);
|
|
|
+ //GLFatal('glBindAttribLocation '+IntToStr(index)+':'+name);
|
|
|
end;
|
|
|
|
|
|
constructor TShader.Create (context: TJSWebGLRenderingContext; vertexShaderSource, fragmentShaderSource: string);
|
|
|
begin
|
|
|
- gl := context;
|
|
|
- vertexShader := CreateShader(gl.VERTEX_SHADER, vertexShaderSource);
|
|
|
- fragmentShader := CreateShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
|
|
|
+ gl := context;
|
|
|
+ vertexShader := CreateShader(gl.VERTEX_SHADER, vertexShaderSource);
|
|
|
+ fragmentShader := CreateShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
|
|
|
end;
|
|
|
|
|
|
-function TShader.CreateShader(theType: GLenum; source: string): TJSWebGLShader;
|
|
|
+function TShader.CreateShader(theType: GLenum; source: string): TJSWebGLShader;
|
|
|
begin
|
|
|
- Result := gl.createShader(theType);
|
|
|
- if Result = nil then
|
|
|
- Fatal('create shader failed');
|
|
|
- gl.shaderSource(Result, source);
|
|
|
- gl.compileShader(Result);
|
|
|
- if gl.getShaderParameter(Result, gl.COMPILE_STATUS) then
|
|
|
- begin
|
|
|
- //writeln('loaded shader ', theType);
|
|
|
- exit;
|
|
|
- end
|
|
|
- else
|
|
|
- begin
|
|
|
- Fatal(gl.getShaderInfoLog(Result));
|
|
|
- //gl.deleteShader(shader);
|
|
|
- end;
|
|
|
+ Result := gl.createShader(theType);
|
|
|
+ if Result = nil then
|
|
|
+ Fatal('create shader failed');
|
|
|
+ gl.shaderSource(Result, source);
|
|
|
+ gl.compileShader(Result);
|
|
|
+ if gl.getShaderParameter(Result, gl.COMPILE_STATUS) then
|
|
|
+ begin
|
|
|
+ //writeln('loaded shader ', theType);
|
|
|
+ exit;
|
|
|
+ end
|
|
|
+ else
|
|
|
+ begin
|
|
|
+ Fatal(gl.getShaderInfoLog(Result));
|
|
|
+ //gl.deleteShader(shader);
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
-procedure TShader.Compile;
|
|
|
+procedure TShader.Compile;
|
|
|
begin
|
|
|
- programID := gl.createProgram;
|
|
|
- gl.attachShader(programID, vertexShader);
|
|
|
- gl.attachShader(programID, fragmentShader);
|
|
|
+ programID := gl.createProgram;
|
|
|
+ gl.attachShader(programID, vertexShader);
|
|
|
+ gl.attachShader(programID, fragmentShader);
|
|
|
end;
|
|
|
|
|
|
-procedure TShader.Link;
|
|
|
+procedure TShader.Link;
|
|
|
begin
|
|
|
gl.linkProgram(programID);
|
|
|
if not gl.getProgramParameter(programID, gl.LINK_STATUS) then
|
|
|
- begin
|
|
|
- Fatal(gl.getProgramInfoLog(programID));
|
|
|
- //gl.deleteProgram(programID);
|
|
|
- end;
|
|
|
+ begin
|
|
|
+ Fatal(gl.getProgramInfoLog(programID));
|
|
|
+ //gl.deleteProgram(programID);
|
|
|
+ end;
|
|
|
end;
|
|
|
|
|
|
-procedure TShader.Use;
|
|
|
+procedure TShader.Use;
|
|
|
begin
|
|
|
- gl.useProgram(programID);
|
|
|
+ gl.useProgram(programID);
|
|
|
end;
|
|
|
|
|
|
end.
|