OBJ是文件,先来解释下OBJ文件。随便找一个OBJ文件,用文本查看:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
# Max2Obj Version
4.0
Mar 10th,
2001
#
# object (
null
) to come ...
#
v -
0.257
0.191
0.423
v -
0.115
0.29
-
0.068
v -
0.237
0
-
0.074
v -
0.237
0.071
0.646
...
#
82
vertices
vt
0.623
0.227
0
vt
0.615
0.53
0
vt
0.895
0.555
0
vt
0.825
0.246
0
vt
0.647
0.847
0
...
#
39
texture vertices
f
1
/
1
2
/
2
3
/
3
f
3
/
3
4
/
4
1
/
1
f
2
/
2
5
/
5
6
/
6
f
6
/
6
3
/
3
2
/
2
...
#
142
faces
|
v:是模型的顶点信息 vt:是贴图的UV坐标 f:是定义面的顶点索引和顶点对应的UV坐标索引 清楚了OBJ文件里各种数据所代表的信息,接下来就是如何把数据传递给显卡渲染。 那么我们需要一个解析OBJ文件的类(代码比较简单,注释就省了)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
package
com.parser
{
import
flash.utils.ByteArray;
public
class
OBJParser
{
private
var
_lines:
Array
;
private
const
LINE_FEED:
String
=
String
.fromCharCode(
10
);
private
const
SPACE:
String
=
String
.fromCharCode(
32
);
private
var
_scale:
Number
;
public
function
OBJParser(objfile:ByteArray,scale:
Number
=
1
)
{
_scale = scale;
if
(objfile)
{
var
lineStr:
String
= parserToStr(objfile);
_lines = lineStr.split(LINE_FEED);
var
loop:
uint
= _lines.length;
for
(
var
i:
uint
=
0
; i < loop; i++)
{
parseLine(_lines[i]);
}
}
}
private
const
VERTEX:
String
=
'v'
;
private
const
UV:
String
=
'vt'
;
private
const
INDEX_DATA:
String
=
'f'
;
private
function
parseLine(lineStr:
String
):
void
{
var
data:
Array
= lineStr.split(SPACE);
if
(!data.length)
return
;
var
key:
String
= data[
0
];
var
parseData:
Array
= data.slice(
1
);
switch
(key)
{
case
VERTEX:
parseVertex(parseData);
break
;
case
UV:
parseUV(parseData);
break
;
case
INDEX_DATA:
parseIndexData(parseData);
break
;
}
}
private
var
_vertices:Vector.<
Number
> =
new
Vector.<
Number
>();
private
function
parseVertex(data:
Array
):
void
{
if
(data[
0
] ==
''
|| data[
0
] ==
" "
)
{
data = data.slice(
1
);
}
var
loop:
uint
= data.length;
for
(
var
i:
uint
=
0
; i < loop; i++)
{
var
value:
Number
=
Number
(data[i]);
_vertices.push(value*_scale);
}
}
private
var
_uvs:Vector.<
Number
> =
new
Vector.<
Number
>();
private
function
parseUV(data:
Array
):
void
{
if
(data[
0
] ==
''
|| data[
0
] ==
" "
)
{
data = data.slice(
1
);
}
var
loop:
uint
=
2
;
for
(
var
i:
uint
=
0
; i < loop; i++)
{
var
value:
Number
=
Number
(data[i]);
_uvs.push(value*_scale);
}
}
private
const
SLASH:
String
=
"/"
;
private
var
_indexData:Vector.<
uint
> =
new
Vector.<
uint
>();
private
var
_vertexsData:Vector.<
Number
> =
new
Vector.<
Number
>();
private
var
_uvData:Vector.<
Number
> =
new
Vector.<
Number
>();
private
var
_faceIndex:
uint
;
private
function
parseIndexData(data:
Array
):
void
{
var
index:
uint
=
0
;
while
((data[index] ==
''
) || (data[index] ==
' '
))index++;
var
loop:
uint
= index+
3
;
var
vertexIndex:
int
;
var
uvIndex:
int
;
var
normalIndex:
int
;
for
(
var
i:
uint
= index; i < loop; i++)
{
var
triplet:
String
= data[i];
var
subdata:
Array
= triplet.split(SLASH);
vertexIndex =
int
(subdata[
0
]) -
1
;
uvIndex =
int
(subdata[
1
]) -
1
;
if
(vertexIndex <
0
) vertexIndex =
0
;
if
(uvIndex <
0
) uvIndex =
0
;
index =
3
*vertexIndex;
_vertexsData.push(_vertices[index +
0
],
_vertices[index +
1
], _vertices[index +
2
]);
index =
2
*uvIndex;
_uvData.push(
1
-_uvs[index+
0
],
1
-_uvs[index+
1
]);
}
_indexData.push(_faceIndex+
0
,_faceIndex+
1
,_faceIndex+
2
);
_faceIndex +=
3
;
}
private
function
parserToStr(objFileByteArray:ByteArray):
String
{
return
objFileByteArray.readUTFBytes(objFileByteArray.bytesAvailable);
}
/**
* 顶点数据
* @return
*
*/
public
function
get
vertexsData():Vector.<
Number
>
{
return
_vertexsData;
}
/**
* UV数据
* @return
*
*/
public
function
get
uvData():Vector.<
Number
>
{
return
_uvData;
}
/**
* 索引数据
* @return
*
*/
public
function
get
indexData():Vector.<
uint
>
{
return
_indexData;
}
}
}
|
1
|
|
有了顶点信息,uv信息和面的索引信息接下来就可以把这货渲染出来了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
|
package
{
import
com.adobe.utils.AGALMiniAssembler;
import
com.adobe.utils.PerspectiveMatrix3D;
import
com.parser.OBJParser;
import
flash.display.Bitmap;
import
flash.display.BitmapData;
import
flash.display.Sprite;
import
flash.display.Stage3D;
import
flash.display.StageAlign;
import
flash.display.StageScaleMode;
import
flash.display3D.Context3D;
import
flash.display3D.Context3DProgramType;
import
flash.display3D.Context3DTextureFormat;
import
flash.display3D.Context3DVertexBufferFormat;
import
flash.display3D.IndexBuffer3D;
import
flash.display3D.Program3D;
import
flash.display3D.VertexBuffer3D;
import
flash.display3D.textures.Texture;
import
flash.events.Event;
import
flash.geom.Matrix;
import
flash.geom.Matrix3D;
import
flash.geom.Vector3D;
import
flash.utils.ByteArray;
[SWF(width=
'800'
,height=
'600'
,backgroundColor=
'0x333333'
,frameRate=
"60"
)]
public
class
GameTest
extends
Sprite
{
[Embed (source =
"art/spaceship.obj"
,
mimeType =
"application/octet-stream"
)]
private
var
objData:Class;
[Embed (source =
"art/spaceship_texture.jpg"
)]
private
var
TextureBitmap:Class;
private
var
textureData:Bitmap =
new
TextureBitmap();
private
var
_stage3D:Stage3D;
private
var
_context3D:Context3D;
private
const
sw:
uint
=
700
;
private
const
sh:
uint
=
500
;
private
var
_objParser:OBJParser;
//顶点缓冲 存储顶点信息
private
var
_vertexBuffer:VertexBuffer3D;
//顶点缓冲 存储UV信息
private
var
_uvBuffer:VertexBuffer3D;
//顶点索引
private
var
_indexBuffer:IndexBuffer3D;
private
var
_texture:Texture;
private
var
_textureSize:
uint
=
512
;
private
var
_projectionmatrix:PerspectiveMatrix3D;
private
var
_viewmatrix:Matrix3D;
private
var
_modelmatrix:Matrix3D =
new
Matrix3D();
private
var
_modelViewProjection:Matrix3D =
new
Matrix3D();
private
var
_vertexShaderAssembler:AGALMiniAssembler;
private
var
_fragmentAssembler:AGALMiniAssembler;
private
var
_program:Program3D;
public
function
GameTest()
{
if
(
this
.stage)
{
init();
}
else
{
addEventListener(Event.ADDED_TO_STAGE,init);
}
}
private
function
init(e:Event =
null
):
void
{
if
(hasEventListener(Event.ADDED_TO_STAGE))removeEventListener(Event.ADDED_TO_STAGE,init);
this
.stage.scaleMode = StageScaleMode.NO_SCALE;
this
.stage.align = StageAlign.TOP_LEFT;
initStage3D();
}
private
function
initStage3D():
void
{
_stage3D =
this
.stage.stage3Ds[
0
];
if
(_stage3D)
{
_stage3D.addEventListener(Event.CONTEXT3D_CREATE,onContext3DCreate);
_stage3D.requestContext3D();
}
}
private
function
onContext3DCreate(e:Event):
void
{
_context3D = _stage3D.context3D;
if
(_context3D ==
null
)
{
throw
new
Error(
"无法创建Context3D"
);
return
;
}
_stage3D.x = (
this
.stage.stageWidth - sw)/
2
;
_stage3D.y = (
this
.stage.stageHeight - sh)/
2
;
_context3D.configureBackBuffer(sw,sh,
1
);
_context3D.clear(
205
,
205
,
205
);
_context3D.enableErrorChecking =
true
;
initData();
initShader();
this
.stage.addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
private
function
initData():
void
{
var
objdata:ByteArray =
new
objData()
as
ByteArray;
_objParser =
new
OBJParser(objdata);
var
vertexCont:
uint
= _objParser.vertexsData.length/
3
;
_vertexBuffer = _context3D.createVertexBuffer(vertexCont,
3
);
_vertexBuffer.uploadFromVector(_objParser.vertexsData,
0
,vertexCont);
var
uvCont:
uint
= _objParser.uvData.length/
2
;
_uvBuffer = _context3D.createVertexBuffer(uvCont,
2
);
_uvBuffer.uploadFromVector(_objParser.uvData,
0
,uvCont);
_context3D.setVertexBufferAt(
0
,_vertexBuffer,
0
,Context3DVertexBufferFormat.FLOAT_3);
_context3D.setVertexBufferAt(
1
,_uvBuffer,
0
,Context3DVertexBufferFormat.FLOAT_2);
var
indexData:Vector.<
uint
> = _objParser.indexData;
_indexBuffer = _context3D.createIndexBuffer(indexData.length);
_indexBuffer.uploadFromVector(indexData,
0
,indexData.length);
_texture = _context3D.createTexture(_textureSize,_textureSize,Context3DTextureFormat.BGRA,
false
);
uploadTextureWithMipmaps(_texture,textureData.bitmapData);
_projectionmatrix =
new
PerspectiveMatrix3D();
_projectionmatrix.identity();
// 45 degrees FOV, 640/480 aspect ratio, 0.1=near, 100=far
_projectionmatrix.perspectiveFieldOfViewRH(
45.0
, sw / sh,
0.01
,
100.0
);
_viewmatrix =
new
Matrix3D();
// camera Matrix3D
_viewmatrix.identity();
// 移动镜头到(0,0,0)
_viewmatrix.appendTranslation(
0
,
0
,
0
);
}
private
function
initShader():
void
{
_vertexShaderAssembler =
new
AGALMiniAssembler();
_vertexShaderAssembler.assemble(Context3DProgramType.VERTEX,
"m44 op, va0, vc0\n"
+
"mov v0, va0\n"
+
"mov v1, va1\n"
);
_fragmentAssembler=
new
AGALMiniAssembler();
_fragmentAssembler..assemble
(
Context3DProgramType.FRAGMENT,
"tex ft0, v1, fs0 <2d,linear,repeat,miplinear>\n"
+
"mov oc, ft0\n"
);
_program = _context3D.createProgram();
_program.upload(_vertexShaderAssembler.agalcode,_fragmentAssembler.agalcode);
_context3D.setTextureAt(
0
,_texture);
_context3D.setProgram(_program);
}
private
var
_t:
Number
=
0
;
private
function
onEnterFrame(e:Event):
void
{
_context3D.clear(
0
,
0
,
0
,.
7
);
_t +=
2.0
;
_modelmatrix.identity();
//旋转模型
_modelmatrix.appendRotation(_t*
1.0
, Vector3D.Y_AXIS);
_modelmatrix.appendRotation(_t*-
0.2
, Vector3D.X_AXIS);
_modelmatrix.appendRotation(_t*
0.3
, Vector3D.Y_AXIS);
_modelmatrix.appendTranslation(-
0.4
,
0
, -
5
);
_modelViewProjection.identity();
_modelViewProjection.append(_modelmatrix);
_modelViewProjection.append(_viewmatrix);
_modelViewProjection.append(_projectionmatrix);
_context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX,
0
, _modelViewProjection,
true
);
//绘制模型
_context3D.drawTriangles(_indexBuffer,
0
, _objParser.indexData.length/
3
);
//呈现至屏幕
_context3D.present();
}
/**
* Mipmap
* @param dest
* @param src
*
*/
private
function
uploadTextureWithMipmaps(dest:Texture, src:BitmapData):
void
{
var
ws:
int
= src.width;
var
hs:
int
= src.height;
var
level:
int
=
0
;
var
tmp:BitmapData;
var
transform:Matrix =
new
Matrix();
tmp =
new
BitmapData(src.width, src.height,
true
,
0
);
while
( ws >=
1
&& hs >=
1
)
{
tmp.draw(src, transform,
null
,
null
,
null
,
true
);
dest.uploadFromBitmapData(tmp, level);
transform.scale(
0.5
,
0.5
);
level++;
ws >>=
1
;
hs >>=
1
;
if
(hs && ws)
{
tmp.dispose();
tmp =
new
BitmapData(ws, hs,
true
,
0x00000000
);
}
}
tmp.dispose();
}
}
}
|