public static Clip ReadClip(ChunkReader reader) { // index var index = reader.ReadUInt32(); // first subchunk header var type = reader.ReadID<ClipType>(); var subchunk_size = reader.ReadUInt16(); subchunk_size += (ushort)(subchunk_size & 1); Clip clip = null; using (var subChunkReader = reader.GetSubChunk(subchunk_size)) { switch (type) { case ClipType.ID_STIL: { var tmp_clip = new ClipStill(index); tmp_clip.Name = subChunkReader.ReadString(); clip = tmp_clip; break; } case ClipType.ID_ISEQ: { var tmp_clip = new ClipSequence(index); tmp_clip.Digits = subChunkReader.ReadUInt8(); tmp_clip.Flags = subChunkReader.ReadUInt8(); tmp_clip.Offset = subChunkReader.ReadSInt16(); subChunkReader.ReadUInt16(); // Legacy Cruft: Nothing to see here tmp_clip.Start = subChunkReader.ReadSInt16(); tmp_clip.End = subChunkReader.ReadSInt16(); tmp_clip.Prefix = subChunkReader.ReadString(); tmp_clip.Suffix = subChunkReader.ReadString(); clip = tmp_clip; break; } case ClipType.ID_ANIM: { var tmp_clip = new ClipAnim(index); tmp_clip.Name = subChunkReader.ReadString(); tmp_clip.Server = subChunkReader.ReadString(); tmp_clip.Data = subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft); clip = tmp_clip; break; } case ClipType.ID_XREF: { var tmp_clip = new ClipCloned(index); tmp_clip.clip_reference_index = subChunkReader.ReadUInt32(); tmp_clip.Name = subChunkReader.ReadString(); clip = tmp_clip; break; } case ClipType.ID_STCC: { var tmp_clip = new ClipColorCycle(index); tmp_clip.lo = subChunkReader.ReadSInt16(); tmp_clip.hi = subChunkReader.ReadSInt16(); tmp_clip.Name = subChunkReader.ReadString(); clip = tmp_clip; break; } default: throw new Exception("Unknown Clip type"); // TODO: create proper exception class for this ... } } while (reader.BytesLeft > 0) { // process subchunks as they're encountered var id = reader.ReadID<ClipDataType>(); subchunk_size = reader.ReadUInt16(); subchunk_size += (ushort)(subchunk_size & 1); using (var subChunkReader = reader.GetSubChunk(subchunk_size)) { switch (id) { //case ClipDataType.ID_CLRS: // Color Space RGB - CLRS { flags[U2], colorspace[U2], filename[FNAM0] } //case ClipDataType.ID_CLRA: // Color Space Alpha - CLRA { flags[U2], colorspace[U2], filename[FNAM0] } //case ClipDataType.ID_FILT: // Image Filtering - FILT { flags[U2] } //case ClipDataType.ID_DITH: // Image Dithering - DITH { flags[U2] } // Contrast - CONT { contrast-delta[FP4], envelope[VX] } case ClipDataType.ID_CONT: { clip.Contrast.value = subChunkReader.ReadSingle(); clip.Contrast.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; } // Brightness - BRIT { brightness-delta[FP4], envelope[VX] } case ClipDataType.ID_BRIT: { clip.Brightness.value = subChunkReader.ReadSingle(); clip.Brightness.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; } // Saturation - SATR { saturation-delta[FP4], envelope[VX] } case ClipDataType.ID_SATR: { clip.Saturation.value = subChunkReader.ReadSingle(); clip.Saturation.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; } // Hue - HUE { hue-rotation[FP4], envelope[VX] } case ClipDataType.ID_HUE: { clip.Hue.value = subChunkReader.ReadSingle(); clip.Hue.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; } // Gamma Correction - GAMM { gamma[F4], envelope[VX] } case ClipDataType.ID_GAMM: { clip.Gamma.value = subChunkReader.ReadSingle(); clip.Gamma.envelope_index = subChunkReader.ReadVariableLengthIndex(); break; } // Negative - NEGA { enable[U2] } case ClipDataType.ID_NEGA: { clip.Negative = subChunkReader.ReadUInt16() != 0; break; } // Time - TIME { start-time[FP4], duration[FP4], frame-rate[FP4] } case ClipDataType.ID_TIME: { clip.StartTime = subChunkReader.ReadSingle(); clip.Duration = subChunkReader.ReadSingle(); clip.FrameRate = subChunkReader.ReadSingle(); break; } // Plug-in Image Filters - IFLT { server-name[S0], flags[U2], data[...] } case ClipDataType.ID_IFLT: // Plug-in Pixel Filters - PFLT { server-name[S0], flags[U2], data[...] } case ClipDataType.ID_PFLT: { var filt = new LightwavePlugin(); filt.name = subChunkReader.ReadString(); filt.flags = subChunkReader.ReadUInt16(); filt.data = subChunkReader.ReadBytes((uint)subChunkReader.BytesLeft); if (id == ClipDataType.ID_IFLT) clip.ImageFilters.Add(filt); else clip.PixelFilters.Add(filt); break; } case ClipDataType.ID_FLAG: // not mentioned in documentation ... var flags = subChunkReader.ReadUInt16(); // unknown what they mean ... break; default: Console.WriteLine("Unknown clip type " + reader.GetIDString((uint)id)); break; } } } return clip; }
static LightwaveObject ReadObject2(ChunkReader reader) { // allocate an object and a default layer var newObject = new LightwaveObject(); var currentLayer = new Layer(); newObject.Layers.Add(currentLayer); bool createdLayer = false; uint pointOffset = 0; uint polygonOffset = 0; uint tagOffset = 0; // process chunks as they're encountered while (reader.BytesLeft > 0) { var id = reader.ReadID<ChunkType>(); var cksize = reader.ReadUInt32(); cksize += cksize & 1; using (var subchunkReader = reader.GetSubChunk(cksize)) { switch (id) { case ChunkType.ID_LAYR: { if (createdLayer) { currentLayer = new Layer(); newObject.Layers.Add(currentLayer); } createdLayer = true; currentLayer.Index = subchunkReader.ReadUInt16(); currentLayer.Flags = subchunkReader.ReadUInt16(); currentLayer.pivot_x = subchunkReader.ReadSingle(); currentLayer.pivot_y = subchunkReader.ReadSingle(); currentLayer.pivot_z = subchunkReader.ReadSingle(); currentLayer.Name = subchunkReader.ReadString(); if (subchunkReader.BytesLeft > 2) currentLayer.Parent = subchunkReader.ReadUInt16(); break; } case ChunkType.ID_PTAG: { ReadPolygonTags(subchunkReader, currentLayer.Polygons, polygonOffset, tagOffset); break; } case ChunkType.ID_BBOX: { currentLayer.bbox_min_x = subchunkReader.ReadSingle(); currentLayer.bbox_min_y = subchunkReader.ReadSingle(); currentLayer.bbox_min_z = subchunkReader.ReadSingle(); currentLayer.bbox_max_x = subchunkReader.ReadSingle(); currentLayer.bbox_max_y = subchunkReader.ReadSingle(); currentLayer.bbox_max_z = subchunkReader.ReadSingle(); break; } case ChunkType.ID_PNTS: { pointOffset = (uint)currentLayer.Points.Count; currentLayer.Points.AddRange( ReadPoints(subchunkReader) // throws exception on failure ); break; } case ChunkType.ID_POLS: { polygonOffset = (uint)currentLayer.Polygons.Count; currentLayer.Polygons.AddRange( ReadPolygons(subchunkReader, pointOffset) // throws exception on failure ); break; } case ChunkType.ID_VMAP: { currentLayer.VertexMaps.Add( VertexMap.ReadVertexMap(subchunkReader, false) // throws exception on failure ); break; } case ChunkType.ID_VMAD: { currentLayer.VertexMaps.Add( VertexMap.ReadVertexMap(subchunkReader, true) // throws exception on failure ); break; } case ChunkType.ID_TAGS: { tagOffset = (uint)newObject.Tags.Count; newObject.Tags.AddRange( LightwaveObject.ReadTags(subchunkReader) // throws exception on failure ); break; } case ChunkType.ID_ENVL: { newObject.Envelopes.Add( Envelope.ReadEnvelope(subchunkReader) // throws exception on failure ); break; } case ChunkType.ID_CLIP: { newObject.Clips.Add( Clip.ReadClip(subchunkReader) // throws exception on failure ); break; } case ChunkType.ID_SURF: { newObject.Surfaces.Add( Surface.ReadSurface(subchunkReader) // throws exception on failure ); break; } case ChunkType.ID_DESC: // Description Line - DESC { description-line[S0] } case ChunkType.ID_TEXT: // Commentary Text - TEXT { comment[S0] } case ChunkType.ID_ICON: // Thumbnail Icon Image - ICON { encoding[U2], width[U2], data[U1] * } case ChunkType.ID_VMPA: // Vertex Map Parameter - VMPA { UV subdivision type[I4], sketch color[I4] } break; default: Console.WriteLine("Unknown chunk type " + reader.GetIDString((uint)id)); break; } } } if (newObject.Tags.Count == 0) throw new Exception("No tags found for this layer"); // TODO: create a proper exception class uint layer_index = 0; foreach (var layer in newObject.Layers) { layer.CalculateBoundingBox(); newObject.CalculatePolygonNormals(layer.Points, layer.Polygons); newObject.ResolvePointPolygons(layer.Points, layer.Polygons); newObject.ResolvePolygonSurfaces(layer.Polygons, newObject.Tags, newObject.Surfaces, layer_index); newObject.CalculateVertexNormals(layer.Points, layer.Polygons); newObject.ResolvePointVertexMaps(layer.Points, layer.VertexMaps); newObject.ResolvePolygonVertexMaps(layer.Polygons, layer.VertexMaps); layer_index++; } return newObject; }
// Returns the contents of a LWOB static LightwaveObject ReadObject5(ChunkReader reader) { // allocate an object and a default layer var newObject = new LightwaveObject(); var currentLayer = new Layer(); newObject.Layers.Add(currentLayer); uint pointOffset = 0; uint polygonOffset = 0; uint tagOffset = 0; // process chunks as they're encountered while (reader.BytesLeft > 0) { var id = reader.ReadID<ChunkType>(); var cksize = reader.ReadUInt32(); cksize += cksize & 1; using (var subchunkReader = reader.GetSubChunk(cksize)) { switch (id) { case ChunkType.ID_PNTS: { pointOffset = (uint)currentLayer.Points.Count; currentLayer.Points.AddRange( ReadPoints(subchunkReader) // throws exception on failure ); break; } case ChunkType.ID_POLS: { polygonOffset = (uint)currentLayer.Polygons.Count; currentLayer.Polygons.AddRange( ReadPolygons5(subchunkReader, pointOffset) // throws exception on failure ); break; } case ChunkType.ID_SRFS: { tagOffset = (uint)newObject.Tags.Count; newObject.Tags.AddRange( LightwaveObject.ReadTags(subchunkReader) // throws exception on failure ); break; } case ChunkType.ID_SURF: { newObject.Surfaces.Add( Surface.ReadSurface5(subchunkReader, newObject) // throws exception on failure ); break; } default: Console.WriteLine("Unknown chunk type " + reader.GetIDString((uint)id)); break; } } } if (newObject.Tags.Count == 0) throw new Exception("No tags found for this layer"); // TODO: create a proper exception class uint layer_index = 0; foreach (var layer in newObject.Layers) { layer.CalculateBoundingBox(); newObject.CalculatePolygonNormals(layer.Points, layer.Polygons); newObject.ResolvePointPolygons(layer.Points, layer.Polygons); newObject.ResolvePolygonSurfaces(layer.Polygons, newObject.Tags, newObject.Surfaces, layer_index); newObject.CalculateVertexNormals(layer.Points, layer.Polygons); layer_index++; } return newObject; }
public static LightwaveObject LoadObject(string filename) { // open the file var contents = File.ReadAllBytes(filename); using (var reader = new ChunkReader(contents)) { // read the first 12 bytes var id = reader.ReadID<HeaderID>(); var formsize = reader.ReadUInt32(); var type = reader.ReadID<ObjectType>(); if (id != HeaderID.ID_FORM) // is this a LW object? return null; using (var formReader = reader.GetSubChunk(formsize)) { switch (type) { case ObjectType.ID_LWO2: return ReadObject2(formReader); case ObjectType.ID_LWOB: return ReadObject5(formReader); default: Console.WriteLine("Unknown object type " + reader.GetIDString((uint)type)); return null; } } } }